A Bayesian is one who, vaguely expecting a horse, and catching a glimpse of a donkey, strongly believes he has seen a mule

Why might someone use Bayesian rather than traditional frequentist?

Quick recap of the ‘Classical Approach’ (‘Frequentist’)

set.seed(0)
p <- 0.5
n <- 10

Let’s say we have a coin, and we want to know the probability that we flip heads. Frequentists assume that there is only one true, fixed, unknown probability that the coin yields heads. For ease of notation, let’s call the constant parameter \(p(\text{head}) = \theta\).

To try and discover the true probability of getting a head, we flip the coin 10 times, and count the number of heads to estimate the probability. Frequentists believe that if we repeated this sampling proceedure of 10 flips many, many times, we should expect the average across all of our samples to be reflective of the true population value (hence why it is called ‘frequentist’).

# Create repeated samples
num_repeated_samples <- 10^3
repeated_samples <- rbinom(n = num_repeated_samples, size = n, prob = p)
repeated_samples.cum_p_mean <- cumsum(repeated_samples)/cumsum(rep(n, num_repeated_samples))

# Cumulative mean tends to p
plot_ly(type = 'scatter', mode = 'lines') %>%
  add_trace(x = 1:length(repeated_samples), y = repeated_samples.cum_p_mean, name = 'Cumulative mean with sampling') %>%
  layout(showlegend = F
         , yaxis = list(range = c(0, 1))
         , title = "Cumulative sample means tends to true p") # , 
k <- round(n*p*0.5,0) # make up fake k that is 'unlikely'
sample_mean <- k/n

binomial_probability <- function(k, n, p) {
  n_choose_k <- factorial(n)/(factorial(k)*factorial(n-k))
  # NOTE - equivalent to gamma_function(k+n-k)/(gamma_function(k)*gamma_function(n-k), or 1/B(k,n-k)
  prob_sample <- n_choose_k*(p)^k*(1-p)^(n-k)
  return(prob_sample)
}

k_heads_maxprob <- round(binomial_probability(k,n,k/n),4)*100
k_heads_actprob <- round(binomial_probability(k,n,p),4)*100
np_heads_actprob <- round(binomial_probability(n*p,n,p),4)*100

But in the real world, it’s not possible to take lots of samples over and over again - and we generally just take one. Taking a sample one time, we might observe \(2\) heads. This is equivalent to creating a sample of size \(10\) and observing a mean probability of \(0.2\).

In frequentist statistics, we assume there is only one true probability for flipping heads (\(\theta\)). We thus want to find that probability that is most likely to give us the result we observed in our sample (a ‘maximum likelihood estimate’). On the left graph below, for every possible value that the probability of flipping heads could be on the x axis (somewhere between 0 and 1), the likelihood of observing \(2\) heads is plotted on the y axis, giving the orange line. For example, the likelihood of observing \(2\) heads if the probablity of flipping heads is \(0.2\) is \(30.2\%\). If the probablity of flipping heads is actually \(0.5\), then the likelihood of flipping 2 heads in our sample is only \(4.39\%\). It appears then, based solely on our sample, that our true probability is most likely to be \(0.2\).

We say estimate, however, because despite treating the true \(\theta\) as a fixed (non-random) value, frequentists acknowledge there is random sampling error. Even if the probability of flipping heads remaining constant, sometimes there will be 2 heads, sometimes there will be 10 heads etc in our sample, simply due to chance. To clarify, frequentists believe the probability is fixed (at \(\theta\)), but the number of heads is a random variable conditioned by that fixed parameter: if \(\theta\) is genuinely \(0.2\), then observing \(2\) heads is more likely than observing \(10\) heads, but both results are possible.

We can illustrate how the number of heads observed follows a probability distribution conditioned by the fixed value of \(\theta\) by plotting the likelihood of observing different numbers of heads. In the graph on the right, the probability of getting different numbers of heads between 0 and 10 is plotted in green if the value of theta is \(0.2\). (A bayesian might also consider other values of theta, such as the blue one if the probability is \(0.5\), but will discuss that later).


# for all the different values of theta, p(H=2|θ)
theta_prob_k_heads <- sapply(seq(0,1,0.01), FUN = function(p) binomial_probability(k=k,n=n,p=p))
# for all the different values of outcome k, p(H=k|θ=0.2)
likelihood_est <- sapply(seq(0,10,1), FUN = function(s) {binomial_probability(k=s,n=n,p=k/n)})
# for all the different values of outcome k, p(H=k|θ=0.5)
likelihood_act <- sapply(seq(0,10,1), FUN = function(s) {binomial_probability(k=s,n=n,p=p)})


subplot(
  plot_ly(type = 'scatter', mode = 'lines') %>%
    add_trace(x = seq(0,1,0.01), y = theta_prob_k_heads, name = paste0('P(H=',k,'|θ)')) %>%
    layout(xaxis = list(title = 'θ'))
  , plot_ly(type = 'scatter', mode = 'lines+markers') %>%
    add_trace(x = seq(0,10,1), y = likelihood_est, name = paste0('p(H=k|θ=',k/n,')')) %>%
    add_trace(x = seq(0,10,1), y = likelihood_act, name = paste0('p(H=k|θ=',p,')'), visible = 'legendonly') %>%
    layout(xaxis = list(title = '# heads'))
  , shareY = T
  , nrows = 1)


# 
# plot_ly(type = 'scatter', mode = 'lines') %>%
#   add_trace(x = x, y = likelihood_est, name = 'Estimated from sample') %>%
#   add_trace(x = x, y = likelihood_act, name = 'Actual likelihood') %>%
#   layout(title = "Likelihood function: X ~ Binom(n,p)"
#          , xaxis = list(title = 'theta')
#          , yaxis = list(title = 'likelihood'))

Thus, to acknowledge that there is randomness associated with this sample mean, frequentists construct confidence intervals. Again to follow the frequentist interpretation, a 95% confidence interval means that, if the sampling proceedure was repeated many, many times, that 95% of the time the true value of \(\theta\) would fall within the confidence interval. (Nuanced note - this is not the same as saying that, in one sample, there is a 95% chance that the true value of \(\theta\) lies within it. This is because \(\theta\) is a fixed value, not a random variable, so it either falls within the sample confidence interval or not.)


true.p.se <- sqrt(p*(1-p)/n)

## BINOMIAL (NORMAL APPROXIMATION)
# The larger the number of successes, np, and failures, n(1-p), the better the normal approximation for confidence intervals (p near 0.5, n as large as possible)
sample.binom.k <- rbinom(n = num_repeated_samples, size = n, prob = p)
sample.binom.p <- sample.binom.k/n
  ## population standard error/confidence interval
    true.binom.p.conf.02.5 <- sample.binom.p-1.96*true.p.se
    true.binom.p.conf.97.5 <- sample.binom.p+1.96*true.p.se
    true.binom.theta_in_conf <- true.binom.p.conf.02.5 < p & true.binom.p.conf.97.5 > p
  ## sample standard error/confidence interval
    sample.binom.p.se <- sqrt(sample.binom.p*(1-sample.binom.p)/n)
    sample.binom.p.conf.02.5 <- sample.binom.p-1.96*sample.binom.p.se
    sample.binom.p.conf.97.5 <- sample.binom.p+1.96*sample.binom.p.se
    sample.binom.theta_in_conf <- sample.binom.p.conf.02.5 < p & sample.binom.p.conf.97.5 > p

## BETA DISTRIBUTION (CONTINUOUS VERSION OF BINOMIAL)
# Since the binomial distribution is non-continuous, we can get confidence intervals closer to 95% from the equivalent beta distribution for the same np & n(1-p)
sample.beta.k <- rbeta(n = num_repeated_samples, shape1 = p*n, shape2 = (1-p)*n)*n
sample.beta.p <- sample.beta.k/n
  ## population standard error/confidence interval
    true.beta.p.conf.02.5 <- sample.beta.p-1.96*true.p.se
    true.beta.p.conf.97.5 <- sample.beta.p+1.96*true.p.se
    true.beta.theta_in_conf <- true.beta.p.conf.02.5 < p & true.beta.p.conf.97.5 > p
  ## sample standard error/confidence interval
    sample.beta.p.se <- sqrt(sample.beta.p*(1-sample.beta.p)/n)
    sample.beta.p.conf.02.5 <- sample.beta.p-1.96*sample.beta.p.se
    sample.beta.p.conf.97.5 <- sample.beta.p+1.96*sample.beta.p.se
    sample.beta.theta_in_conf <- sample.beta.p.conf.02.5 < p & sample.beta.p.conf.97.5 > p

# First one-time sample confidence intervals
one_time.sample.p.se <- sqrt((k/n)*(1-k/n)/n)
one_time_sample.conf_02.5 <- (k/n)-1.96*one_time.sample.p.se
one_time_sample.conf_97.5 <- (k/n)+1.96*one_time.sample.p.se

# One time sample either contains true value of theta or not
plot_ly(type = 'scatter', mode = 'lines') %>%
  add_trace(x = rep(c(one_time_sample.conf_02.5, one_time_sample.conf_97.5), each = 3), y = c(1,-1,0,0,-1,1), name = 'Confidence interval') %>%
  add_trace(x = p, y = 0, mode = 'markers', name = 'True value', marker = list(size=50, symbol = 'x')) %>%
  layout(showlegend = F
         , yaxis = list(range = c(-2, 2), zeroline = F, showticklabels = F)
         , xaxis = list(range = c(one_time_sample.conf_02.5 - 0.1, one_time_sample.conf_97.5 + 0.1))
         , title = "One-time sample confidence interval either contains true value of theta or not")


# Cumulative confidence intervals contain 95% of the time
plot_ly(type = 'scatter', mode = 'lines') %>%
  add_trace(x = 1:num_repeated_samples
            , y = cumsum(true.beta.theta_in_conf)/cumsum(rep(1,num_repeated_samples))
            , name = 'Cumulative mean with sampling') %>%
  layout(showlegend = F
         #, yaxis = list(range = c(0, 1))
         , title = "Cumulative confidence intervals tends to contain p 95% of the time")

NA

In order to obtain these results, certain conditions have to be met, including:

Okay, so what exactly does a Bayesian approach mean?

Bayesian inference is broadly similar to frequentist inference. The key difference though is that, under a Bayesian approach, the true value of \(\theta\) is treated as a random variable, rather than a fixed constant.

So, for example, we might believe that in our economy, \(90\)% of coins are fair, and \(10\)% of coins are biased, with biased coins flipping heads \(20\)% of the time.

Under the guise of being frequentists previously, we had no prior belief as to what \(\theta\) would be: we only made an educated guess based on the 2 heads we observed. We believed that there is one fixed value of the true \(\theta\), and that there was only randomness associated with sampling, so we created a confidence interval to portray this.

Now, as bayesians, we are also incorporating our prior beliefs as to what \(\theta\) is - that it is either fair or biased. We believe that there is not just random error associated in sampling - we also treat \(\theta\) as an additional random variable, since we don’t know whether our coin is fair or biased either, and fair coins will have a different sample mean distribution than biased ones.

fair_coin_k_prob <- binomial_probability(k, n, p)
biased_coin_k_prob <- binomial_probability(k, n, k/n)

Given that we observe 2 heads, the likelihood of this occuring if the coin is fair is \(4%\)%, and \(30%\)% if the coin is biased. In the absence of informative prior information then, it might appear that the coin is more likely to be biased.

However, we also need to take into account that \(90\)% of coins in the population are fair, so even before observing 2 heads, we have some belief about whether the coin is fair or biased (i.e. the probability distribution associated with the random variable \(\theta\)).

fair_coin_prob <- (fair_coin_k_prob * 0.9)/((fair_coin_k_prob * 0.9) + (biased_coin_k_prob * 0.1))
biased_coin_prob <- (biased_coin_k_prob * 0.1)/((fair_coin_k_prob * 0.9) + (biased_coin_k_prob * 0.1))

# data.frame(
# matrix(data = c(binomial_probability(2,10,0.5)*0.9 # p(fair, 2 heads)
#                 , (1-binomial_probability(2,10,0.5))*0.9 # p(fair, not 2 heads)
#                 , 0.9 # p(fair)
#                 , binomial_probability(2,10,0.2)*0.1 # biased, 2 heads
#                 , (1-binomial_probability(2,10,0.2))*0.1 # biased, not 2 heads
#                 , 0.1
#                 , binomial_probability(2,10,0.5)*0.9 + binomial_probability(2,10,0.2)*0.1
#                 , (1-binomial_probability(2,10,0.5))*0.9 + (1-binomial_probability(2,10,0.2))*0.1
#                 , 1
#                 )
#        , nrow = 3
#        , dimnames = list(c('Flip 2 heads', 'Not flip 2 heads', 'Total')
#                          , c('Fair', 'Biased', 'Total'))
#        )
# )

..and so we can find the likelihood we got a fair coin, given the fact we observed 2 heads:

\[ P(\text{fair}|H=2) =\frac{P(\text{H=2 and fair})}{P(\text{H=2 and fair})+P(\text{H=2 and biased})} =\frac{P(\text{H=2|fair}) \times P(\text{fair})}{P(\text{H=2})} \]

\[ P(\text{fair}|H=2) =\frac{0.0439 \times 0.9}{0.0439 + 0.302}=0.567 \]

\[ P(\text{biased}|H=2) =\frac{P(\text{H=2 and biased})}{P(\text{H=2 and fair})+P(\text{H=2 and biased})} =\frac{P(\text{H=2|biased}) \times P(\text{biased})}{P(\text{H=2})} \] \[ P(\text{biased}|H=2) =\frac{0.302 \times 0.1}{0.0439 + 0.302}=0.433 \]

In other words, our prior belief (before sampling and observing any data) was that the likelihood of our coin having \(\theta\) equal to \(0.5\) was \(90\%\), and the likelihood of it having theta equal to \(0.2\) was \(10\%\). After observing 2 heads, we update our beliefs, so that the likelihood of the probability of our coin flipping heads (\(\theta\)) being equal to \(0.5\) is now equal to \(56.7\%\), and of it being equal to \(0.2\) being \(43.3\%\)

It is this incorporation of prior data that is either seen as Bayesian’s biggest advantage or pitfall. On the one hand, experiments are not abstract devices, and some knowledge about the process being investigated before obtaining the data is known and arguably should be incorporated. On the other, incorporating subjective opinions, particularly strong ones, may mean that you do not learn the true values you are trying to derive. Bayesian is thus analysis that uses a set of observations to change opinion rather than as a means to determine ultimate truth.

To help describe bayesian analysis, the following terms are often used:

And thus, we can then describe Bayes Theorem:

\[ P(\theta|X) =\frac{P(X|\theta) \times P(\theta)}{P(X|\theta) \times P(\theta) + P(X|\theta') \times P(\theta')} =\frac{P(X|\theta) \times P(\theta)}{P(X)} \]

Thinking about Bayesian in terms of predicting getting a head next time:

There are two sources of uncertainty when building statistical models to predict things:

Classical models deal with point 1, making a point estimate in the future based on the ‘best’ parameters estimated from past data. This is equivalent to forming a likelihood (i.e. incorporating new data). What they don’t do though is think about point 2, that the seemingly optimum model estimated itself might be incorrect since the true values of \(\theta\) are not fixed, and this is equivalent to not building in any prior beliefs. In a way, by giving a point estimate, the models generate a false sense of precision, and the confidence intervals arounds any estimate is only predicated on the idea there is sampling randomness, rather than randomness associated with \(\theta\) itself.

In other words, classical models seek to best fit the new evidence (traditionally through maximum likelihood estimation for generalized linear models), whereas bayesian models incorporate prior beliefs as well.

To predict the likelihood of getting heads next time, given our evidence, we do the following:

\[ \begin{aligned} P(\text{heads}|k=2) \\ = P(\text{heads}|\text{fair},k=2) \times P(\text{fair}|k=2) + P(\text{heads}|\text{biased},k=2) \times P(\text{biased}|k=2) \\ = 0.5 \times P(\text{fair}|k=2) + 0.2 \times P(\text{biased}|k=2) \\ = 0.5 \times \frac{P(k=2|\text{fair}) \times P(\text{fair})}{P(k=2)} + 0.2 \times \frac{P(k=2|\text{biased}) \times P(\text{biased})}{P(k=2)} \\ = 0.5 \times 0.567 + 0.2 \times 0.433 \\ = 0.3701 \end{aligned} \]

Note that we have subbed in our results for the probability that we have a fair or a biased coin using bayes theorem, to allow us to predict the probability we get heads next time.

If we chose a purely frequentist approach, we would have guessed that the likelihood is \(0.2\), since we observed \(2\) heads, and can only take one value (and this was the maximum likelihood estimate). If we chose to only use our prior informtion that 90% of coins were fair, we would have guessed \(0.9 \times 0.5 + 0.1 \times 0.2 = 0.47\). However, since we have incorporated both the prior information and the new evidence, we predict a result that is somewhere in the middle, at \(0.3701\).

We can formalise this:

\[ f(y|x)= \int f(y|\theta,x)f(\theta|x) \partial(\theta) \] In other words, for all our potential estimates of what \(\theta\) could be (\(\delta(\theta)\)), how likely each of those values for \(\theta\) might be given the number of heads we have observed (\(f(X|\theta)\)), and how likely we were to observe our number of heads given those parameters \(P(y|\theta,x)\), we can make a prediction for what our number of heads might be in the next flip.

This now allows us to tackle problems where the prior comes from continuous distributions.

Continuous prior beliefs

In the last example, we had a very strong prior - \(\theta\) could only take 2 values, and was 90% likely to be fair, so it had a very large influence on the result. However, Bayesian tends to most valuable where both the new data and past prior have more equal influence, and the posterior forming a distribution that sits somewhere between that estimated by the prior and likelihood function.

So - let’s say the last time we tried flipping 10 times, we got heads 5 times (our prior) and the new coin flipped heads 2 times (our new evidence). We can then get the following equations:

Our prior is a binomial:

\[ \text{Prior: } P(\theta) \sim \text{Binom}[n_{1},k_{1}/n_1] = \text{Binom}[10,0.5] \] Our likelihood is also binomial:

\[ \text{Likelhood: } P(X|\theta) \sim\text{Binom}[n_{2},k_{2}/n_2] = \text{Binom}[10,0.2] \] Our posterior thus follows a binomial distribution too:

\[ \text{Posterior: } P(\theta|X) = \frac{P(X|\theta)P(\theta)}{\int P(X|\theta)P(\theta) d\theta} \approx \text{Binom}\left[n_1+n_2,\frac{k_1+k_2}{n_1+n_2}\right] = \text{Binom}[7,0.35] \\ \]

In other words, we obtain our posterior estimate by multiplying our likelihood function and prior beliefs, and dividing it by all possible probabilities given our evidence that theta could take. In practice, this denominator just ensures that our probabilities sum to one.

We can plot this to see what is looks like:

# prior <- sapply(x, FUN = function(theta) {beta_probability(theta,k1+1,n1-k1+1)/(n1+1)})
# likelihood_f <- sapply(x, FUN = function(theta) {beta_probability(theta,k2+1,n2-k2+1)/(n2+1)})
# posterior <- sapply(x, FUN = function(theta) {beta_probability(theta,k1+k2+2,n1-k1+1+n2-k2+1)/(n1+n2+2)})

# prior <- dbeta(x, k1+1, n1-k1+1)/(n1+1)
# likelihood_f <- dbeta(x, k2+1, n2-k2+1)/(n2+1)
# posterior <- dbeta(x, k1+k2+1, n1-k1+n2-k2+1)/(n1+n2+1)

prior <- dbinom(seq(0,10,1), n1, k1/n1)
likelihood_f <- dbinom(seq(0,10,1), n2, k2/n2)
posterior <- dbinom(seq(0,20,2), n1+n2, (k1+k2)/(n1+n2))*2

plot_ly(type = 'scatter', mode = 'lines+markers') %>%
  add_trace(x = seq(0,10,1), y = prior, name = 'Prior belief: Horse') %>%
  add_trace(x = seq(0,10,1), y = likelihood_f, name = 'Likelihood (New data): Donkey') %>%
  add_trace(x = seq(0,10,1), y = posterior, name = 'Posterior Belief: Mule') %>%
  layout(title = "Updating Prior Beliefs")

The posterior curve is both taller and narrow than both the distributions estimated from the prior and the likelihood functions. This is because, by including both prior data and new evidence, our sample size has increased, so this reflects greater confidence that the observed number of heads,conditioned by the probability distribution of \(\theta\), will lie within a smaller interval.

However, in general, we might have a continuous prior or likelihood function, and the beta distribution is a continuous functional form of the binomial distribution.

A quick introduction to the beta distribution: it can model lots of different functional forms using \(a\) and \(b\) as parameters, by inputting them into the following function:

\[ \text{Beta}[a,b] \sim \theta^{a-1}\times(1-\theta)^{b-1} \]

Now let’s define the binomial distribution for comparison:

\[ \text{Binom}(n,p) = {n \choose k } [\theta^{k}\times(1-\theta)^{n-k}] \]

Now to get geeky - you might have already noticed how eerily similar the beta distribution is to the binomial distribution from the formula - and in fact you can use the beta distribution as a continous function equivalent to the binomial function.

\[ p(\text{H=}k| \theta) \sim \theta^k\times(1-\theta)^{n-k} \approx \text{Beta}[k+1,n-k+1] \]

Both binomial and beta likelihoods are scaled though (normalized), so that the sum of their probabilites total 1. For the beta distribution, the scaling is achieved like this:

\[ Beta[\alpha,\beta] = \theta^{\alpha-1}\times(1-\theta)^{\beta-1} \times \frac{1}{\text{B}(\alpha,\beta)} \] Where

\[ \frac{1}{\text{B}(\alpha,\beta)} = \frac{\Gamma(\alpha+\beta)}{\Gamma(\alpha) \times \Gamma(\beta)} = \frac{(\alpha+\beta-1)!}{(\alpha-1)! \times (\beta-1)!} \]

So if successes \(\alpha = k+1\) and failures \(\beta = n - k + 1\), then:

\[ \frac{(\alpha+\beta-1)!}{(\alpha-1)! \times (\beta-1)!}=\frac{((k+1)+(n-k+1)-1)!}{((k+1)-1)! \times ((n-k+1)-1)!}=\frac{(n+1)!}{(k)!\times(n-k)!} = {n+1 \choose k } = (n+1) \times {n \choose k } \]

And thus:

\[ Beta[k+1,n-k+1] = (n+1) \times \text{Binom}(n,p) \]

This relationship helps us combine binomial likelihoods and beta priors to create a posterior in what is called conjugancy. Conjugancy occurs when the posterior distribution is in the same family of probability density functions as the prior belief, but with new parameter values (updated to reflect what has been learned from the data). The posterior comes from the same family as the prior, rather than the data’s distribution.

So - if we want to use a beta distribution for our binomial prior, we can model this using a beta distribution by setting \(a\) and \(b\) to both equal to 1

\[ \text{Prior: } p(\theta) \approx \text{Beta}[k_{1}+1,n_{1}-k_{1}+1] = \text{Beta}[6,10-5+1] = \text{Beta}[5,6] \]

\[ \text{Likelihood: } p(k|\theta)=\text{Binom}[n_{2},k_{2}/n_2] = \text{Beta}[k_{2}+1,n_{2}-k_{2}+1] = \text{Beta}[2+1,10-2+1] = \text{Beta}[3,9] \]

\[ \text{Posterior: } p(\theta|k) \approx \\ \text{Beta}[5,6] \times \text{Beta}[3,9] = \\ \text{Beta}[k_1+k_{2}+2,n_1-k_1+n_{2}-k_{2}+2] = \text{Beta}[9,15] \]

# prior <- sapply(x, FUN = function(theta) {beta_probability(theta,k1+1,n1-k1+1)/(n1+1)})
# likelihood_f <- sapply(x, FUN = function(theta) {beta_probability(theta,k2+1,n2-k2+1)/(n2+1)})
# posterior <- sapply(x, FUN = function(theta) {beta_probability(theta,k1+k2+2,n1-k1+1+n2-k2+1)/(n1+n2+2)})

# prior <- dbeta(x, k1+1, n1-k1+1)/(n1+1)
# likelihood_f <- dbeta(x, k2+1, n2-k2+1)/(n2+1)
# posterior <- dbeta(x, k1+k2+1, n1-k1+n2-k2+1)/(n1+n2+1)

x <- seq(0, 1, length=100+1)

prior <- dbeta(x, k1+1, n1-k1+1)#/(n1+1)
likelihood_f <- dbeta(x, k2+1, n2-k2+1)#/(n2+1)
posterior <- dbeta(x, k1+k2+2, n1-k1+n2-k2+2)#/((n1+1)*(n2+1))
# prior <- prior/sum(prior)
# likelihood_f <- likelihood_f/sum(likelihood_f)
# posterior <- posterior/sum(posterior)

plot_ly(type = 'scatter', mode = 'lines') %>%
  add_trace(x = x, y = prior, name = 'Prior belief: Horse') %>%
  add_trace(x = x, y = likelihood_f, name = 'Likelihood (New data): Donkey') %>%
  add_trace(x = x, y = posterior, name = 'Posterior Belief: Mule') %>%
  layout(title = "Updating Prior Beliefs")

Why might we want to use beta though rather than just binomial as our prior? Well a beta distribution is really flexible, so we can use it to model a range of different priors depending on previous experiments, or even as a way of regularising through the bayesian paradigm.

So - if we want to create a uniform distribution for our prior, we can model this using a beta distribution by setting \(a\) and \(b\) to both equal to 1

\[ p(\theta) \sim \text{Beta}[1,1] \sim \theta^{1-1}\times(1-\theta)^{1-1}=\theta^{0}\times(1-\theta)^{0}= 1 \text{ (for all values of }\theta) \]

And we can model our likelihood using the binomial function

\[ p(X| \theta) = p(\text{H=}k| \theta) = \text{Binom}(n,p) \sim \theta^k\times(1-\theta)^{n-k} \]

And finally - the posterior is proportional to the prior multiplied by the likelihood:

\[ p(\theta) \times p(X| \theta) \sim [\theta^{a-1}\times(1-\theta)^{b-1}] \times [\theta^k\times(1-\theta)^{n-k}] = \theta^{a-1+k}\times(1-\theta)^{b-1+n-k} \]

Which we can purely parametize in a beta distribution (you can see that, the stronger the prior - and hence the larger a and b - the smaller the impact new evidence k and n has on the posterior distribution):

\[ \theta^{a-1+k}\times(1-\theta)^{b-1+n-k} \sim \text{Beta}[a+k,b+n-k] \]


gamma_function <- function(n) {
  return(factorial(n-1))
}

beta_probability <- function(theta,a,b) {
  numerator <- ((theta)^(a-1))*((1-theta)^(b-1))
  denominator <- (gamma_function(a)*gamma_function(b))/gamma_function(a+b)
  return(numerator/denominator)
}

a <- 1
b <- 1

# uniform distribution
# prior <- dbeta(x = x, shape1 = 1, shape2 = 1)
x <- seq(0, 1, length=100+1)
prior <- sapply(x, FUN = function(theta) {beta_probability(theta,a,b)})

# binomial distribution
likelihood_f.binom <- sapply(x, FUN = function(theta) {binomial_probability(k,n,theta)})
likelihood_f.binom.int <- likelihood_f.binom
likelihood_f.binom.int[round(x*n,0) != x*n] <- NA

# beta distribution
likelihood_f.beta <- sapply(x, FUN = function(theta) {beta_probability(theta,k+1,n-k+1)*1/(n+1)})

posterior <- sapply(x, FUN = function(theta) {beta_probability(theta,a+k+1,b+n-k+1)*1/(n+1)})

plot_ly(type = 'scatter', mode = 'lines') %>%
  add_trace(x = x, y = prior/sum(prior), name = 'Prior: Uniform = Beta(1,1)') %>%
  add_trace(x = x, y = likelihood_f.binom.int, name = 'Likelihood: ~ Binom(k=2,p=theta,n)', mode = 'markers') %>%
  add_trace(x = x, y = likelihood_f.beta, name = 'Likelihood: ~ Beta(p=theta,k,n-k)') %>%
  add_trace(x = x, y = posterior, name = 'Posterior: ~ Beta(a+k,b+n-k)') %>%
  layout(xaxis = list(title = 'Theta'))
Ignoring 91 observationsIgnoring 91 observations

As you can see, with a very ‘objective’ prior, the Bayesian posterior is very similar to the result derived from the frequentist evidence-only distribution (the Bayesian likelihood function).

Markov-chain Monte-Carlo (MCMC): Estimating posterior distributions when the prior/likelihood density functions are non-conjugant

Sometimes though, we have priors and likelihood functions that do not play nicely with each other, and hence we cannot define the posterior density function analytically. In this instance, we can make many draws from the posterior distribution for various values of \(\theta\) to infer what the posterior distribution looks like.

This is the ‘monte-carlo’ bit - we randomly choose values of \(\theta\) to make draws from the posterior from.

However, the aim of this is to create a histogram that most closely resembles the posterior distibution that we are trying to estimate. For this histogram to be reflective of the posterior distribution, we need to take draws more frequently from the areas higher density from the posterior, and less frequently where they are of lower density.

This is where the ‘markov-chain’ bit comes in - when making a new draw, whether we add this new value to the histogram, or the old one again to the histogram, depends on the probability density associated with the new point vs the old one.

The instructions for the MCMC algorithm (using the Metropolis-Hastings method) is as follows:

\[ \text{for(i in num_draws) } \{\\ \hspace{3cm}\theta_{new,i}:= X \sim N(\theta_{recorded,i-1},\sigma^2) \\ \hspace{3cm}posterior_{new,i}:= P(X|\theta_{new,i}) \times P(\theta_{new,i}) \\ \hspace{3cm}posterior_{old,i}:= P(X|\theta_{old,i}) \times P(\theta_{old,i}) \\ \hspace{3cm}r_{num,i}:= r_i \sim \text{unif}(0,1)\\ \hspace{3cm} \text{if}\left(\frac{posterior_{new,i}}{posterior_{old,i}} > r_{num,i}\right) \{\\ \hspace{6cm}\theta_{recorded,i}:=\theta_{new,i}\\ \hspace{6cm}\theta_{recorded,i}:=\theta_{old,i}\\ \hspace{3cm}\} \\ \} \]

What does this mean?

Let’s take our example before, where our prior had 5 of 10 flips, and our evidence was 2 of 10 flips, to check how well this works. We run the algorithm 10,000 times (in general use, we might exclude a lot first draws, called the burn, but in this instance as long as we take enough samples it should still create a reflective histogram).

As a reminder:

## MCMC M-H
theta_v <- c()
for(i in 1:10^5) {
  if(i == 1) {
    theta_old <- rnorm(1,p,0.05)
    theta_new <- rnorm(1,p,0.05)
  } else {
    theta_old <- chosen_theta
  }
  theta_new <- rnorm(1,theta_old,0.05)
  if(theta_new < 0 | theta_new > 1) {
    break
  }
  posterior_new <- dbeta(theta_new,k1+1,n1-k1+1) * dbinom(k2,n2,theta_new) # new prior * likelihood
  posterior_old <- dbeta(theta_old,k1+1,n1-k1+1) * dbinom(k2,n2,theta_old) # old prior * likelihood
  if(posterior_new/posterior_old > runif(1)) {
    chosen_theta <- theta_new
  } else {
    chosen_theta <- theta_old
  }
  theta_v <- c(theta_v,chosen_theta)
}

plot_ly() %>%
  add_trace(x=theta_v,type='histogram',name='frequency') %>%
  add_trace(x=density(theta_v)$x, y=density(theta_v)$y,type='scatter',mode='lines',yaxis='y2',name='Density approximation') %>%
  add_trace(x=seq(0,1,0.01), y=dbeta(seq(0,1,0.01),k1+k2+2,n1+n2-k1-k2+2),type='scatter',mode='lines',yaxis='y2',name=paste0('True posterior: Beta(',k1+k2+2,',',n1-k1+n2-k2+2,')')) %>%
  layout(yaxis2=list(overlaying="y",side="right",rangemode = "tozero"))

Thinking about Bayesian in terms of predicting getting a head next time (continuous prior):

So now we are set up to make a prediction for getting a heads next time, given our prior beliefs and our new evidence.

Let’s remind ourselves of the equation to make predictions:

\[ f(y|x)= \int f(y|\theta,x)f(\theta|x) \partial(\theta) \]

We now know \(p(\theta|x)\) - our posterior distribution. So now we just need to estimate \(p(y|\theta,x)\). So how do we do this?

Well we know that \(y\) follows a bernoulli distribution, and we can solve this through maximum likelihood estimation. In particular, for binary predictions, we often assume that the likelihood function follows a logistic distribution, given as \(p(y|\theta,X)=(1+e^{-X\beta})^{-1}\) - You can see this in another note I have written here.

Great, so just need to integrate the product of \(f(y|\theta,x)\) and \(f(\theta|x)\) with respect to \(\theta\).

y_dist <- c()
theta_options <- seq(0,1,0.01)
for(i in theta_options) {
  theta_posterior_dist = dbeta(i,k1+k2+2,n1-k1+n2-k2+2)
  y_logit = (1+exp(-(k2)*theta_posterior_dist))^(-1)
  y_dist <- c(y_dist,theta_posterior_dist*y_logit)
}

plot_ly() %>%
  add_trace(x=seq(0,1,0.01), y=y_dist,type='scatter',mode='lines',name=paste0('P(y|X)')) %>%
  layout(title='Likelihood of observing heads next time')

NA

This is more interesting if we had varying explanatory variables \(X\) - and then we can derive different posterior distributions for \(y\) given different values for those explanatory features.

Bayesian Credible intervals and HPD (need to change this to \(y\))

So now we have our posterior distribution for \(\y\), how might we summarise our new understanding as to where \(\y\) might lie in the range of?

This leads on to Bayesian Credible intervals - the Bayesian equivalent to frequentist’s confidence intervals. Because Bayesian estimates distributions rather than fixed values, credible intervals are arguably more intuitive than confidence intervals: they reflect the a (95%) probability that of here the value generated by the random variable theta will fall within the range (rather than the frequentist interpretation that the true value will fall within the confidence interval 95% of the time).

Like a confidence interval, a credible interval is symmetric around the mean, and spans the x-axis enough to cover 95% of the area under the probability distribution curve. And that is good if the posterior distribution is also symmetric - but as can be seen in our example above, that is not necessarily the case, where the posterior is skewed.

If the distribution is skewed then, it could be better to compute the region of highest posterior density (HPD) - which by definition will be the narrowest interval across the potential values for theta, but cover 95% of the area under the probability curve. In other words, we still maintain the same 95% probability of theta lying within the region, but the region is the narrowest it possibly can be.

So to do this, we want to find the interval between \(\theta_{low}\) and \(\theta_{high}\) such at:

\[ \int_{\theta_{low}}^{\theta_{high}} p(\theta|x) d\theta= 1-\alpha \]

But finding this can be tricky, particularly with computing closed intervals. The code below shows a brute force solution. The way to think about this is as follows:

plot_ly(type = 'scatter', mode = 'lines') %>%
  add_trace(x = x, y = y_dist, name = 'Beta(9,15)') %>%
  add_trace(x = rep(0.36,2), y = c(0,max(sapply(highest_posterior_density, FUN = function(x) {dbeta(x,k1+k2+2,n1-k1+n2-k2+2)}))), name = 'Mean') %>%
  add_trace(x = pmin(pmax(x, credible_intervals[1]),credible_intervals[2]), y = likelihood_f, name = 'Credible Interval', fill = 'tozeroy', line = list(dash = 'dot')) %>%
  layout(title = 'Credible Interval (equidistant around mean)')


plot_ly(type = 'scatter', mode = 'lines') %>%
  add_trace(x = seq(0,1,0.001), y = sapply(seq(0,1,0.001), FUN = function(x) {dbeta(x,k1+k2+2,n1-k1+n2-k2+2)}), name = 'Beta(9,15)') %>%
  add_trace(x = rep(0.36,2), y = c(0,max(sapply(highest_posterior_density, FUN = function(x) {dbeta(x,k1+k2+2,n1-k1+n2-k2+2)}))), name = 'Mean') %>%
  add_trace(x = highest_posterior_density, y = sapply(highest_posterior_density, FUN = function(x) {dbeta(x,k1+k2+2,n1-k1+n2-k2+2)}), name = 'Highest Posterior Density', fill = 'tozeroy', line = list(dash = 'dot')) %>%
  layout(title = 'Highest Posterior Density (smallest spread that contains 95% of data)')

The width of the credible interval is larger, ranging from 0.1883926 to 0.5616074 (range of 0.3732147), whereas width the range of the HPD is smaller, ranging from 0.186 to 0.56 (range of 0.374)

Some Bayesians argue though that credible intervals and HPDs are only really something that are useful to compare to frequentist interpretations, as the distribution is already given by the posterior.

LS0tDQp0aXRsZTogIkJheWVzaWFuIFN0YXRpc3RpY3MiDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgc21hcnQ6IGZhbHNlDQogIGh0bWxfZG9jdW1lbnQ6IGRlZmF1bHQNCiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0DQotLS0NCg0KYGBge3Igc2V0dXAsIGVjaG8gPSBGQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldCgNCglmaWcuaGVpZ2h0ID0gNywNCglmaWcud2lkdGggPSAxMCwNCgltZXNzYWdlID0gRkFMU0UsDQoJd2FybmluZyA9IEZBTFNFDQopDQpyZXF1aXJlKHBsb3RseSkNCnJlcXVpcmUoc2NhbGVzKQ0KYGBgDQoNCipBIEJheWVzaWFuIGlzIG9uZSB3aG8sIHZhZ3VlbHkgZXhwZWN0aW5nIGEgaG9yc2UsIGFuZCBjYXRjaGluZyBhIGdsaW1wc2Ugb2YgYSBkb25rZXksIHN0cm9uZ2x5IGJlbGlldmVzIGhlIGhhcyBzZWVuIGEgbXVsZSoNCg0KV2h5IG1pZ2h0IHNvbWVvbmUgdXNlIEJheWVzaWFuIHJhdGhlciB0aGFuIHRyYWRpdGlvbmFsIGZyZXF1ZW50aXN0Pw0KDQoqIElmIHRoZXkgaGF2ZSBwcmlvciBiZWxpZWZzIGFzIHRvIHRoZSB0cnV0aCBiZWZvcmUgYW4gZXhwZXJpbWVudCBpcyBjb25kdWN0ZWQNCiogSWYgeW91IHdhbnQgdG8gcHJlZGljdCBob3cgdGhlIGRpc3RyaWJ1dGlvbiBvZiBhIHBvcHVsYXRpb24gbWlnaHQgY2hhbmdlLCByYXRoZXIgdGhhbiBhIHBvaW50IGVzdGltYXRlLiBGb3IgZXhhbXBsZSwgdHJhZGl0aW9uYWxseSwgd2UgbWlnaHQgdHJ5IGFuZCBwcmVkaWN0IHRoZSBtZWFuIG51bWJlciBvZiBwZW9wbGUgd2hvIGNob29zZSB0byBvcmRlciBhZnRlciBnb2luZyBvbiBvdXIgd2Vic2l0ZS4gDQoNCiMjIFF1aWNrIHJlY2FwIG9mIHRoZSAnQ2xhc3NpY2FsIEFwcHJvYWNoJyAoJ0ZyZXF1ZW50aXN0JykNCg0KYGBge3J9DQpzZXQuc2VlZCgwKQ0KcCA8LSAwLjUNCm4gPC0gMTANCmBgYA0KDQpMZXQncyBzYXkgd2UgaGF2ZSBhIGNvaW4sIGFuZCB3ZSB3YW50IHRvIGtub3cgdGhlIHByb2JhYmlsaXR5IHRoYXQgd2UgZmxpcCBoZWFkcy4gRnJlcXVlbnRpc3RzIGFzc3VtZSB0aGF0IHRoZXJlIGlzIG9ubHkgb25lIHRydWUsIGZpeGVkLCB1bmtub3duIHByb2JhYmlsaXR5IHRoYXQgdGhlIGNvaW4geWllbGRzIGhlYWRzLiBGb3IgZWFzZSBvZiBub3RhdGlvbiwgbGV0J3MgY2FsbCB0aGUgY29uc3RhbnQgcGFyYW1ldGVyICRwKFx0ZXh0e2hlYWR9KSA9IFx0aGV0YSQuDQoNClRvIHRyeSBhbmQgZGlzY292ZXIgdGhlIHRydWUgcHJvYmFiaWxpdHkgb2YgZ2V0dGluZyBhIGhlYWQsIHdlIGZsaXAgdGhlIGNvaW4gYHIgbmAgdGltZXMsIGFuZCBjb3VudCB0aGUgbnVtYmVyIG9mIGhlYWRzIHRvIGVzdGltYXRlIHRoZSBwcm9iYWJpbGl0eS4gRnJlcXVlbnRpc3RzIGJlbGlldmUgdGhhdCBpZiB3ZSByZXBlYXRlZCB0aGlzIHNhbXBsaW5nIHByb2NlZWR1cmUgb2YgMTAgZmxpcHMgbWFueSwgbWFueSB0aW1lcywgd2Ugc2hvdWxkIGV4cGVjdCB0aGUgYXZlcmFnZSBhY3Jvc3MgYWxsIG9mIG91ciBzYW1wbGVzIHRvIGJlIHJlZmxlY3RpdmUgb2YgdGhlIHRydWUgcG9wdWxhdGlvbiB2YWx1ZSAoaGVuY2Ugd2h5IGl0IGlzIGNhbGxlZCAnZnJlcXVlbnRpc3QnKS4gDQoNCmBgYCB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTd9DQojIENyZWF0ZSByZXBlYXRlZCBzYW1wbGVzDQpudW1fcmVwZWF0ZWRfc2FtcGxlcyA8LSAxMF4zDQpyZXBlYXRlZF9zYW1wbGVzIDwtIHJiaW5vbShuID0gbnVtX3JlcGVhdGVkX3NhbXBsZXMsIHNpemUgPSBuLCBwcm9iID0gcCkNCnJlcGVhdGVkX3NhbXBsZXMuY3VtX3BfbWVhbiA8LSBjdW1zdW0ocmVwZWF0ZWRfc2FtcGxlcykvY3Vtc3VtKHJlcChuLCBudW1fcmVwZWF0ZWRfc2FtcGxlcykpDQoNCiMgQ3VtdWxhdGl2ZSBtZWFuIHRlbmRzIHRvIHANCnBsb3RfbHkodHlwZSA9ICdzY2F0dGVyJywgbW9kZSA9ICdsaW5lcycpICU+JQ0KICBhZGRfdHJhY2UoeCA9IDE6bGVuZ3RoKHJlcGVhdGVkX3NhbXBsZXMpLCB5ID0gcmVwZWF0ZWRfc2FtcGxlcy5jdW1fcF9tZWFuLCBuYW1lID0gJ0N1bXVsYXRpdmUgbWVhbiB3aXRoIHNhbXBsaW5nJykgJT4lDQogIGxheW91dChzaG93bGVnZW5kID0gRg0KICAgICAgICAgLCB5YXhpcyA9IGxpc3QocmFuZ2UgPSBjKDAsIDEpKQ0KICAgICAgICAgLCB0aXRsZSA9ICJDdW11bGF0aXZlIHNhbXBsZSBtZWFucyB0ZW5kcyB0byB0cnVlIHAiKSAjICwgDQpgYGANCg0KYGBgIHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9N30NCmsgPC0gcm91bmQobipwKjAuNSwwKSAjIG1ha2UgdXAgZmFrZSBrIHRoYXQgaXMgJ3VubGlrZWx5Jw0Kc2FtcGxlX21lYW4gPC0gay9uDQoNCmJpbm9taWFsX3Byb2JhYmlsaXR5IDwtIGZ1bmN0aW9uKGssIG4sIHApIHsNCiAgbl9jaG9vc2VfayA8LSBmYWN0b3JpYWwobikvKGZhY3RvcmlhbChrKSpmYWN0b3JpYWwobi1rKSkNCiAgIyBOT1RFIC0gZXF1aXZhbGVudCB0byBnYW1tYV9mdW5jdGlvbihrK24taykvKGdhbW1hX2Z1bmN0aW9uKGspKmdhbW1hX2Z1bmN0aW9uKG4tayksIG9yIDEvQihrLG4taykNCiAgcHJvYl9zYW1wbGUgPC0gbl9jaG9vc2VfayoocCleayooMS1wKV4obi1rKQ0KICByZXR1cm4ocHJvYl9zYW1wbGUpDQp9DQoNCmtfaGVhZHNfbWF4cHJvYiA8LSByb3VuZChiaW5vbWlhbF9wcm9iYWJpbGl0eShrLG4say9uKSw0KSoxMDANCmtfaGVhZHNfYWN0cHJvYiA8LSByb3VuZChiaW5vbWlhbF9wcm9iYWJpbGl0eShrLG4scCksNCkqMTAwDQpucF9oZWFkc19hY3Rwcm9iIDwtIHJvdW5kKGJpbm9taWFsX3Byb2JhYmlsaXR5KG4qcCxuLHApLDQpKjEwMA0KDQpgYGANCg0KQnV0IGluIHRoZSByZWFsIHdvcmxkLCBpdCdzIG5vdCBwb3NzaWJsZSB0byB0YWtlIGxvdHMgb2Ygc2FtcGxlcyBvdmVyIGFuZCBvdmVyIGFnYWluIC0gYW5kIHdlIGdlbmVyYWxseSBqdXN0IHRha2Ugb25lLiBUYWtpbmcgYSBzYW1wbGUgb25lIHRpbWUsIHdlIG1pZ2h0IG9ic2VydmUgJGByIGtgJCBoZWFkcy4gVGhpcyBpcyBlcXVpdmFsZW50IHRvIGNyZWF0aW5nIGEgc2FtcGxlIG9mIHNpemUgJGByIG5gJCBhbmQgb2JzZXJ2aW5nIGEgbWVhbiBwcm9iYWJpbGl0eSBvZiAkYHIgc2FtcGxlX21lYW5gJC4NCg0KSW4gZnJlcXVlbnRpc3Qgc3RhdGlzdGljcywgd2UgYXNzdW1lIHRoZXJlIGlzIG9ubHkgb25lIHRydWUgcHJvYmFiaWxpdHkgZm9yIGZsaXBwaW5nIGhlYWRzICgkXHRoZXRhJCkuIFdlIHRodXMgd2FudCB0byBmaW5kIHRoYXQgcHJvYmFiaWxpdHkgdGhhdCBpcyBtb3N0IGxpa2VseSB0byBnaXZlIHVzIHRoZSByZXN1bHQgd2Ugb2JzZXJ2ZWQgaW4gb3VyIHNhbXBsZSAoYSAnbWF4aW11bSBsaWtlbGlob29kIGVzdGltYXRlJykuIE9uIHRoZSBsZWZ0IGdyYXBoIGJlbG93LCBmb3IgZXZlcnkgcG9zc2libGUgdmFsdWUgdGhhdCB0aGUgcHJvYmFiaWxpdHkgb2YgZmxpcHBpbmcgaGVhZHMgY291bGQgYmUgb24gdGhlIHggYXhpcyAoc29tZXdoZXJlIGJldHdlZW4gMCBhbmQgMSksIHRoZSBsaWtlbGlob29kIG9mIG9ic2VydmluZyAkYHIga2AkIGhlYWRzIGlzIHBsb3R0ZWQgb24gdGhlIHkgYXhpcywgZ2l2aW5nIHRoZSBvcmFuZ2UgbGluZS4gRm9yIGV4YW1wbGUsIHRoZSBsaWtlbGlob29kIG9mIG9ic2VydmluZyAkYHIga2AkIGhlYWRzIGlmIHRoZSBwcm9iYWJsaXR5IG9mIGZsaXBwaW5nIGhlYWRzIGlzICRgciBrL25gJCBpcyAkYHIga19oZWFkc19tYXhwcm9iYFwlJC4gSWYgdGhlIHByb2JhYmxpdHkgb2YgZmxpcHBpbmcgaGVhZHMgaXMgYWN0dWFsbHkgJGByIHBgJCwgdGhlbiB0aGUgbGlrZWxpaG9vZCBvZiBmbGlwcGluZyBgciBrYCBoZWFkcyBpbiBvdXIgc2FtcGxlIGlzIG9ubHkgJGByIGtfaGVhZHNfYWN0cHJvYmBcJSQuIEl0IGFwcGVhcnMgdGhlbiwgYmFzZWQgc29sZWx5IG9uIG91ciBzYW1wbGUsIHRoYXQgb3VyIHRydWUgcHJvYmFiaWxpdHkgaXMgbW9zdCBsaWtlbHkgdG8gYmUgJGByIHNhbXBsZV9tZWFuYCQuDQoNCldlIHNheSBlc3RpbWF0ZSwgaG93ZXZlciwgYmVjYXVzZSBkZXNwaXRlIHRyZWF0aW5nIHRoZSB0cnVlICRcdGhldGEkIGFzIGEgZml4ZWQgKG5vbi1yYW5kb20pIHZhbHVlLCBmcmVxdWVudGlzdHMgYWNrbm93bGVkZ2UgdGhlcmUgaXMgcmFuZG9tIHNhbXBsaW5nIGVycm9yLiBFdmVuIGlmIHRoZSBwcm9iYWJpbGl0eSBvZiBmbGlwcGluZyBoZWFkcyByZW1haW5pbmcgY29uc3RhbnQsIHNvbWV0aW1lcyB0aGVyZSB3aWxsIGJlIGByIGtgIGhlYWRzLCBzb21ldGltZXMgdGhlcmUgd2lsbCBiZSBgciBuYCBoZWFkcyBldGMgaW4gb3VyIHNhbXBsZSwgc2ltcGx5IGR1ZSB0byBjaGFuY2UuIFRvIGNsYXJpZnksIGZyZXF1ZW50aXN0cyBiZWxpZXZlIHRoZSBwcm9iYWJpbGl0eSBpcyBmaXhlZCAoYXQgJFx0aGV0YSQpLCBidXQgdGhlIG51bWJlciBvZiBoZWFkcyBpcyBhIHJhbmRvbSB2YXJpYWJsZSBjb25kaXRpb25lZCBieSB0aGF0IGZpeGVkIHBhcmFtZXRlcjogaWYgJFx0aGV0YSQgaXMgZ2VudWluZWx5ICRgciBzYW1wbGVfbWVhbmAkLCB0aGVuIG9ic2VydmluZyAkYHIga2AkIGhlYWRzIGlzIG1vcmUgbGlrZWx5IHRoYW4gb2JzZXJ2aW5nICQxMCQgaGVhZHMsIGJ1dCBib3RoIHJlc3VsdHMgYXJlIHBvc3NpYmxlLiANCg0KV2UgY2FuIGlsbHVzdHJhdGUgaG93IHRoZSBudW1iZXIgb2YgaGVhZHMgb2JzZXJ2ZWQgZm9sbG93cyBhIHByb2JhYmlsaXR5IGRpc3RyaWJ1dGlvbiBjb25kaXRpb25lZCBieSB0aGUgZml4ZWQgdmFsdWUgb2YgJFx0aGV0YSQgYnkgcGxvdHRpbmcgdGhlIGxpa2VsaWhvb2Qgb2Ygb2JzZXJ2aW5nIGRpZmZlcmVudCBudW1iZXJzIG9mIGhlYWRzLiBJbiB0aGUgZ3JhcGggb24gdGhlIHJpZ2h0LCB0aGUgcHJvYmFiaWxpdHkgb2YgZ2V0dGluZyBkaWZmZXJlbnQgbnVtYmVycyBvZiBoZWFkcyBiZXR3ZWVuIDAgYW5kIGByIG5gIGlzIHBsb3R0ZWQgaW4gZ3JlZW4gaWYgdGhlIHZhbHVlIG9mIHRoZXRhIGlzICRgciBrL25gJC4gKEEgYmF5ZXNpYW4gbWlnaHQgYWxzbyBjb25zaWRlciBvdGhlciB2YWx1ZXMgb2YgdGhldGEsIHN1Y2ggYXMgdGhlIGJsdWUgb25lIGlmIHRoZSBwcm9iYWJpbGl0eSBpcyAkYHIgcGAkLCBidXQgd2lsbCBkaXNjdXNzIHRoYXQgbGF0ZXIpLg0KDQpgYGAge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03fQ0KDQojIGZvciBhbGwgdGhlIGRpZmZlcmVudCB2YWx1ZXMgb2YgdGhldGEsIHAoSD0yfM64KQ0KdGhldGFfcHJvYl9rX2hlYWRzIDwtIHNhcHBseShzZXEoMCwxLDAuMDEpLCBGVU4gPSBmdW5jdGlvbihwKSBiaW5vbWlhbF9wcm9iYWJpbGl0eShrPWssbj1uLHA9cCkpDQojIGZvciBhbGwgdGhlIGRpZmZlcmVudCB2YWx1ZXMgb2Ygb3V0Y29tZSBrLCBwKEg9a3zOuD0wLjIpDQpsaWtlbGlob29kX2VzdCA8LSBzYXBwbHkoc2VxKDAsMTAsMSksIEZVTiA9IGZ1bmN0aW9uKHMpIHtiaW5vbWlhbF9wcm9iYWJpbGl0eShrPXMsbj1uLHA9ay9uKX0pDQojIGZvciBhbGwgdGhlIGRpZmZlcmVudCB2YWx1ZXMgb2Ygb3V0Y29tZSBrLCBwKEg9a3zOuD0wLjUpDQpsaWtlbGlob29kX2FjdCA8LSBzYXBwbHkoc2VxKDAsMTAsMSksIEZVTiA9IGZ1bmN0aW9uKHMpIHtiaW5vbWlhbF9wcm9iYWJpbGl0eShrPXMsbj1uLHA9cCl9KQ0KDQoNCnN1YnBsb3QoDQogIHBsb3RfbHkodHlwZSA9ICdzY2F0dGVyJywgbW9kZSA9ICdsaW5lcycpICU+JQ0KICAgIGFkZF90cmFjZSh4ID0gc2VxKDAsMSwwLjAxKSwgeSA9IHRoZXRhX3Byb2Jfa19oZWFkcywgbmFtZSA9IHBhc3RlMCgnUChIPScsaywnfM64KScpKSAlPiUNCiAgICBsYXlvdXQoeGF4aXMgPSBsaXN0KHRpdGxlID0gJ864JykpDQogICwgcGxvdF9seSh0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ2xpbmVzK21hcmtlcnMnKSAlPiUNCiAgICBhZGRfdHJhY2UoeCA9IHNlcSgwLDEwLDEpLCB5ID0gbGlrZWxpaG9vZF9lc3QsIG5hbWUgPSBwYXN0ZTAoJ3AoSD1rfM64PScsay9uLCcpJykpICU+JQ0KICAgIGFkZF90cmFjZSh4ID0gc2VxKDAsMTAsMSksIHkgPSBsaWtlbGlob29kX2FjdCwgbmFtZSA9IHBhc3RlMCgncChIPWt8zrg9JyxwLCcpJyksIHZpc2libGUgPSAnbGVnZW5kb25seScpICU+JQ0KICAgIGxheW91dCh4YXhpcyA9IGxpc3QodGl0bGUgPSAnIyBoZWFkcycpKQ0KICAsIHNoYXJlWSA9IFQNCiAgLCBucm93cyA9IDEpDQoNCiMgDQojIHBsb3RfbHkodHlwZSA9ICdzY2F0dGVyJywgbW9kZSA9ICdsaW5lcycpICU+JQ0KIyAgIGFkZF90cmFjZSh4ID0geCwgeSA9IGxpa2VsaWhvb2RfZXN0LCBuYW1lID0gJ0VzdGltYXRlZCBmcm9tIHNhbXBsZScpICU+JQ0KIyAgIGFkZF90cmFjZSh4ID0geCwgeSA9IGxpa2VsaWhvb2RfYWN0LCBuYW1lID0gJ0FjdHVhbCBsaWtlbGlob29kJykgJT4lDQojICAgbGF5b3V0KHRpdGxlID0gIkxpa2VsaWhvb2QgZnVuY3Rpb246IFggfiBCaW5vbShuLHApIg0KIyAgICAgICAgICAsIHhheGlzID0gbGlzdCh0aXRsZSA9ICd0aGV0YScpDQojICAgICAgICAgICwgeWF4aXMgPSBsaXN0KHRpdGxlID0gJ2xpa2VsaWhvb2QnKSkNCmBgYA0KDQpUaHVzLCB0byBhY2tub3dsZWRnZSB0aGF0IHRoZXJlIGlzIHJhbmRvbW5lc3MgYXNzb2NpYXRlZCB3aXRoIHRoaXMgc2FtcGxlIG1lYW4sIGZyZXF1ZW50aXN0cyBjb25zdHJ1Y3QgY29uZmlkZW5jZSBpbnRlcnZhbHMuIEFnYWluIHRvIGZvbGxvdyB0aGUgZnJlcXVlbnRpc3QgaW50ZXJwcmV0YXRpb24sIGEgOTUlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgbWVhbnMgdGhhdCwgaWYgdGhlIHNhbXBsaW5nIHByb2NlZWR1cmUgd2FzIHJlcGVhdGVkIG1hbnksIG1hbnkgdGltZXMsIHRoYXQgOTUlIG9mIHRoZSB0aW1lIHRoZSB0cnVlIHZhbHVlIG9mICRcdGhldGEkIHdvdWxkIGZhbGwgd2l0aGluIHRoZSBjb25maWRlbmNlIGludGVydmFsLg0KKihOdWFuY2VkIG5vdGUgLSB0aGlzIGlzIG5vdCB0aGUgc2FtZSBhcyBzYXlpbmcgdGhhdCwgaW4gb25lIHNhbXBsZSwgdGhlcmUgaXMgYSA5NSUgY2hhbmNlIHRoYXQgdGhlIHRydWUgdmFsdWUgb2YgJFx0aGV0YSQgbGllcyB3aXRoaW4gaXQuIFRoaXMgaXMgYmVjYXVzZSAkXHRoZXRhJCBpcyBhIGZpeGVkIHZhbHVlLCBub3QgYSByYW5kb20gdmFyaWFibGUsIHNvIGl0IGVpdGhlciBmYWxscyB3aXRoaW4gdGhlIHNhbXBsZSBjb25maWRlbmNlIGludGVydmFsIG9yIG5vdC4pKg0KDQpgYGAge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03fQ0KDQp0cnVlLnAuc2UgPC0gc3FydChwKigxLXApL24pDQoNCiMjIEJJTk9NSUFMIChOT1JNQUwgQVBQUk9YSU1BVElPTikNCiMgVGhlIGxhcmdlciB0aGUgbnVtYmVyIG9mIHN1Y2Nlc3NlcywgbnAsIGFuZCBmYWlsdXJlcywgbigxLXApLCB0aGUgYmV0dGVyIHRoZSBub3JtYWwgYXBwcm94aW1hdGlvbiBmb3IgY29uZmlkZW5jZSBpbnRlcnZhbHMgKHAgbmVhciAwLjUsIG4gYXMgbGFyZ2UgYXMgcG9zc2libGUpDQpzYW1wbGUuYmlub20uayA8LSByYmlub20obiA9IG51bV9yZXBlYXRlZF9zYW1wbGVzLCBzaXplID0gbiwgcHJvYiA9IHApDQpzYW1wbGUuYmlub20ucCA8LSBzYW1wbGUuYmlub20uay9uDQogICMjIHBvcHVsYXRpb24gc3RhbmRhcmQgZXJyb3IvY29uZmlkZW5jZSBpbnRlcnZhbA0KICAgIHRydWUuYmlub20ucC5jb25mLjAyLjUgPC0gc2FtcGxlLmJpbm9tLnAtMS45Nip0cnVlLnAuc2UNCiAgICB0cnVlLmJpbm9tLnAuY29uZi45Ny41IDwtIHNhbXBsZS5iaW5vbS5wKzEuOTYqdHJ1ZS5wLnNlDQogICAgdHJ1ZS5iaW5vbS50aGV0YV9pbl9jb25mIDwtIHRydWUuYmlub20ucC5jb25mLjAyLjUgPCBwICYgdHJ1ZS5iaW5vbS5wLmNvbmYuOTcuNSA+IHANCiAgIyMgc2FtcGxlIHN0YW5kYXJkIGVycm9yL2NvbmZpZGVuY2UgaW50ZXJ2YWwNCiAgICBzYW1wbGUuYmlub20ucC5zZSA8LSBzcXJ0KHNhbXBsZS5iaW5vbS5wKigxLXNhbXBsZS5iaW5vbS5wKS9uKQ0KICAgIHNhbXBsZS5iaW5vbS5wLmNvbmYuMDIuNSA8LSBzYW1wbGUuYmlub20ucC0xLjk2KnNhbXBsZS5iaW5vbS5wLnNlDQogICAgc2FtcGxlLmJpbm9tLnAuY29uZi45Ny41IDwtIHNhbXBsZS5iaW5vbS5wKzEuOTYqc2FtcGxlLmJpbm9tLnAuc2UNCiAgICBzYW1wbGUuYmlub20udGhldGFfaW5fY29uZiA8LSBzYW1wbGUuYmlub20ucC5jb25mLjAyLjUgPCBwICYgc2FtcGxlLmJpbm9tLnAuY29uZi45Ny41ID4gcA0KDQojIyBCRVRBIERJU1RSSUJVVElPTiAoQ09OVElOVU9VUyBWRVJTSU9OIE9GIEJJTk9NSUFMKQ0KIyBTaW5jZSB0aGUgYmlub21pYWwgZGlzdHJpYnV0aW9uIGlzIG5vbi1jb250aW51b3VzLCB3ZSBjYW4gZ2V0IGNvbmZpZGVuY2UgaW50ZXJ2YWxzIGNsb3NlciB0byA5NSUgZnJvbSB0aGUgZXF1aXZhbGVudCBiZXRhIGRpc3RyaWJ1dGlvbiBmb3IgdGhlIHNhbWUgbnAgJiBuKDEtcCkNCnNhbXBsZS5iZXRhLmsgPC0gcmJldGEobiA9IG51bV9yZXBlYXRlZF9zYW1wbGVzLCBzaGFwZTEgPSBwKm4sIHNoYXBlMiA9ICgxLXApKm4pKm4NCnNhbXBsZS5iZXRhLnAgPC0gc2FtcGxlLmJldGEuay9uDQogICMjIHBvcHVsYXRpb24gc3RhbmRhcmQgZXJyb3IvY29uZmlkZW5jZSBpbnRlcnZhbA0KICAgIHRydWUuYmV0YS5wLmNvbmYuMDIuNSA8LSBzYW1wbGUuYmV0YS5wLTEuOTYqdHJ1ZS5wLnNlDQogICAgdHJ1ZS5iZXRhLnAuY29uZi45Ny41IDwtIHNhbXBsZS5iZXRhLnArMS45Nip0cnVlLnAuc2UNCiAgICB0cnVlLmJldGEudGhldGFfaW5fY29uZiA8LSB0cnVlLmJldGEucC5jb25mLjAyLjUgPCBwICYgdHJ1ZS5iZXRhLnAuY29uZi45Ny41ID4gcA0KICAjIyBzYW1wbGUgc3RhbmRhcmQgZXJyb3IvY29uZmlkZW5jZSBpbnRlcnZhbA0KICAgIHNhbXBsZS5iZXRhLnAuc2UgPC0gc3FydChzYW1wbGUuYmV0YS5wKigxLXNhbXBsZS5iZXRhLnApL24pDQogICAgc2FtcGxlLmJldGEucC5jb25mLjAyLjUgPC0gc2FtcGxlLmJldGEucC0xLjk2KnNhbXBsZS5iZXRhLnAuc2UNCiAgICBzYW1wbGUuYmV0YS5wLmNvbmYuOTcuNSA8LSBzYW1wbGUuYmV0YS5wKzEuOTYqc2FtcGxlLmJldGEucC5zZQ0KICAgIHNhbXBsZS5iZXRhLnRoZXRhX2luX2NvbmYgPC0gc2FtcGxlLmJldGEucC5jb25mLjAyLjUgPCBwICYgc2FtcGxlLmJldGEucC5jb25mLjk3LjUgPiBwDQoNCiMgRmlyc3Qgb25lLXRpbWUgc2FtcGxlIGNvbmZpZGVuY2UgaW50ZXJ2YWxzDQpvbmVfdGltZS5zYW1wbGUucC5zZSA8LSBzcXJ0KChrL24pKigxLWsvbikvbikNCm9uZV90aW1lX3NhbXBsZS5jb25mXzAyLjUgPC0gKGsvbiktMS45NipvbmVfdGltZS5zYW1wbGUucC5zZQ0Kb25lX3RpbWVfc2FtcGxlLmNvbmZfOTcuNSA8LSAoay9uKSsxLjk2Km9uZV90aW1lLnNhbXBsZS5wLnNlDQoNCiMgT25lIHRpbWUgc2FtcGxlIGVpdGhlciBjb250YWlucyB0cnVlIHZhbHVlIG9mIHRoZXRhIG9yIG5vdA0KcGxvdF9seSh0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ2xpbmVzJykgJT4lDQogIGFkZF90cmFjZSh4ID0gcmVwKGMob25lX3RpbWVfc2FtcGxlLmNvbmZfMDIuNSwgb25lX3RpbWVfc2FtcGxlLmNvbmZfOTcuNSksIGVhY2ggPSAzKSwgeSA9IGMoMSwtMSwwLDAsLTEsMSksIG5hbWUgPSAnQ29uZmlkZW5jZSBpbnRlcnZhbCcpICU+JQ0KICBhZGRfdHJhY2UoeCA9IHAsIHkgPSAwLCBtb2RlID0gJ21hcmtlcnMnLCBuYW1lID0gJ1RydWUgdmFsdWUnLCBtYXJrZXIgPSBsaXN0KHNpemU9NTAsIHN5bWJvbCA9ICd4JykpICU+JQ0KICBsYXlvdXQoc2hvd2xlZ2VuZCA9IEYNCiAgICAgICAgICwgeWF4aXMgPSBsaXN0KHJhbmdlID0gYygtMiwgMiksIHplcm9saW5lID0gRiwgc2hvd3RpY2tsYWJlbHMgPSBGKQ0KICAgICAgICAgLCB4YXhpcyA9IGxpc3QocmFuZ2UgPSBjKG9uZV90aW1lX3NhbXBsZS5jb25mXzAyLjUgLSAwLjEsIG9uZV90aW1lX3NhbXBsZS5jb25mXzk3LjUgKyAwLjEpKQ0KICAgICAgICAgLCB0aXRsZSA9ICJPbmUtdGltZSBzYW1wbGUgY29uZmlkZW5jZSBpbnRlcnZhbCBlaXRoZXIgY29udGFpbnMgdHJ1ZSB2YWx1ZSBvZiB0aGV0YSBvciBub3QiKQ0KDQojIEN1bXVsYXRpdmUgY29uZmlkZW5jZSBpbnRlcnZhbHMgY29udGFpbiA5NSUgb2YgdGhlIHRpbWUNCnBsb3RfbHkodHlwZSA9ICdzY2F0dGVyJywgbW9kZSA9ICdsaW5lcycpICU+JQ0KICBhZGRfdHJhY2UoeCA9IDE6bnVtX3JlcGVhdGVkX3NhbXBsZXMNCiAgICAgICAgICAgICwgeSA9IGN1bXN1bSh0cnVlLmJldGEudGhldGFfaW5fY29uZikvY3Vtc3VtKHJlcCgxLG51bV9yZXBlYXRlZF9zYW1wbGVzKSkNCiAgICAgICAgICAgICwgbmFtZSA9ICdDdW11bGF0aXZlIG1lYW4gd2l0aCBzYW1wbGluZycpICU+JQ0KICBsYXlvdXQoc2hvd2xlZ2VuZCA9IEYNCiAgICAgICAgICMsIHlheGlzID0gbGlzdChyYW5nZSA9IGMoMCwgMSkpDQogICAgICAgICAsIHRpdGxlID0gIkN1bXVsYXRpdmUgY29uZmlkZW5jZSBpbnRlcnZhbHMgdGVuZHMgdG8gY29udGFpbiBwIDk1JSBvZiB0aGUgdGltZSIpDQoNCmBgYA0KDQpJbiBvcmRlciB0byBvYnRhaW4gdGhlc2UgcmVzdWx0cywgY2VydGFpbiBjb25kaXRpb25zIGhhdmUgdG8gYmUgbWV0LCBpbmNsdWRpbmc6DQoNCiogVGhlIHJhbmRvbWx5IHNhbXBsZWQgb2JzZXJ2YXRpb25zIGFyZSBpbmRlcGVuZGVudCwgc28gb2JzZXJ2aW5nIGFueSBvbmUgdmFsdWUgZG9lcyBub3QgaGF2ZSBhbiBpbXBhY3Qgb24gd2hhdCBzdWJzZXF1ZW50IHZhbHVlcyB3ZSBvYnNlcnZlIG1pZ2h0IGJlLiBJbiBvdXIgZXhhbXBsZSwgZmxpcHBpbmcgYSBoZWFkIG9uY2UgZG9lcyBub3QgaW1wYWN0IHRoZSBsaWtlbGlob29kIG9mIGZsaXBwaW5nIGl0IGFnYWluLiAqKElmIHJ1bm5pbmcgYW4gZXhwZXJpbWVudCBpbiBwcmFjdGljZSwgdGhpcyBtaWdodCBtZWFuIHRoYXQgb3VyIHBvcHVsYXRpb24gaXMgc3VmZmljaWVudGx5IGxhcmdlIHdoZW4gY29tcGFyZWQgdG8gb3VyIHNhbXBsZSB0aGF0IHdlIGRvbid0IG5lZWQgdG8gYmUgY29uY2VybmVkIHdpdGggcmVwbGFjZW1lbnQuIEFub3RoZXIgdGhpbmcgdG8gd2F0Y2ggb3V0IGZvciBpcyBuZXR3b3JrIGVmZmVjdHMgLSBpbWFnaW5lIGFuIGFpcmxpbmUgaW5jcmVhc2VzIHByaWNlIG9mIHNlYXRzIGFzIHRoZXkgc2VsbC4gU2F5IHdlIHdhbnQgdG8gZXhwZXJpbWVudCBpbiBvZmZlcmluZyBzb21lIGN1c3RvbWVycyBhIHNwZWNpYWwgb2ZmZXIgZm9yIGFpcmxpbmUgdGlja2V0cywgb3VyIHZhcmlhbnQsIGFuZCBzb21lIG5vdCwgb3VyIGNvbnRyb2w6IHRoZSBjdXN0b21lcnMgd2l0aCB0aGUgc3BlY2lhbCBvZmZlciBtaWdodCBiZSBtb3JlIGxpa2VseSB0byBidXksIGFuZCB0aGlzIHdpbGwgc2hvcnRlbiB0aGUgc3VwcGx5LCBpbmNyZWFzaW5nIHRoZSBwcmljZSBmb3IgdGhvc2Ugd2l0aG91dCB0aGUgc3BlY2lhbCBvZmZlciwgYW5kIGRlY3JlYXNpbmcgdGhlaXIgbGlrZWxpaG9vZCB0byBidXkpLioNCiogQW55IHNhbXBsZWQgb2JzZXJ2YXRpb25zIGFyZSBpZGVudGljYWxseSBkaXN0cmlidXRlZCB0byB0aGUgcG9wdWxhdGlvbiAqKGUuZy4gaWYgd2UgdGFrZSBvdXIgc2FtcGxlIGZyb20gdGhlIFVTQSwgdGhleSBtaWdodCBoYXZlIGEgZGlmZmVyZW50IHByb3BlbnNpdHkgdG8gd2F0Y2ggVFYgdGhhbiB0aGUgVUsuKSoNCiogQmVjYXVzZSBpdCBpcyBhIHJhbmRvbSBzYW1wbGUsIHRoZSB2YWx1ZXMgb2JzZXJ2ZWQgYXJlIGNoYXJhY3RlcmlzZWQgYnkgYSBwcm9iYWJpbGl0eSBkaXN0cmlidXRpb24gYXNzb2NpYXRlZCB3aXRoICRcdGhldGEkLiAqKGUuZy4gd2UgZG9uJ3Qga25vdyB3aGF0IHRoZSBudW1iZXIgb2YgaGVhZHMgb2JlcnZlZCB3aWxsIGJlIGJlZm9yZSBzYW1wbGluZywgYnV0IHRoZSBsaWtlbGlob29kIG9mIHdoYXQgd2Ugb2JzZXJ2ZSBpcyBwcmUtZGV0ZXJtaW5lZCB0aHJvdWdoIGl0cyBhc3NvY2lhdGVkIHByb2JhYmlsaXR5IGRpc3RyaWJ1dGlvbiB3aXRoICRcdGhldGEkKSoNCg0KIyMgT2theSwgc28gd2hhdCBleGFjdGx5IGRvZXMgYSBCYXllc2lhbiBhcHByb2FjaCBtZWFuPw0KDQpCYXllc2lhbiBpbmZlcmVuY2UgaXMgYnJvYWRseSBzaW1pbGFyIHRvIGZyZXF1ZW50aXN0IGluZmVyZW5jZS4gKipUaGUga2V5IGRpZmZlcmVuY2UgdGhvdWdoIGlzIHRoYXQsIHVuZGVyIGEgQmF5ZXNpYW4gYXBwcm9hY2gsIHRoZSB0cnVlIHZhbHVlIG9mICRcdGhldGEkIGlzIHRyZWF0ZWQgYXMgYSByYW5kb20gdmFyaWFibGUsIHJhdGhlciB0aGFuIGEgZml4ZWQgY29uc3RhbnQuKiogDQoNClNvLCBmb3IgZXhhbXBsZSwgd2UgbWlnaHQgYmVsaWV2ZSB0aGF0IGluIG91ciBlY29ub215LCAkOTAkJSBvZiBjb2lucyBhcmUgZmFpciwgYW5kICQxMCQlIG9mIGNvaW5zIGFyZSBiaWFzZWQsIHdpdGggYmlhc2VkIGNvaW5zIGZsaXBwaW5nIGhlYWRzICRgciBrKjEwYCQlIG9mIHRoZSB0aW1lLiANCg0KVW5kZXIgdGhlIGd1aXNlIG9mIGJlaW5nIGZyZXF1ZW50aXN0cyBwcmV2aW91c2x5LCB3ZSBoYWQgbm8gcHJpb3IgYmVsaWVmIGFzIHRvIHdoYXQgJFx0aGV0YSQgd291bGQgYmU6IHdlIG9ubHkgbWFkZSBhbiBlZHVjYXRlZCBndWVzcyBiYXNlZCBvbiB0aGUgYHIga2AgaGVhZHMgd2Ugb2JzZXJ2ZWQuIFdlIGJlbGlldmVkIHRoYXQgdGhlcmUgaXMgb25lIGZpeGVkIHZhbHVlIG9mIHRoZSB0cnVlICRcdGhldGEkLCBhbmQgdGhhdCB0aGVyZSB3YXMgb25seSByYW5kb21uZXNzIGFzc29jaWF0ZWQgd2l0aCBzYW1wbGluZywgc28gd2UgY3JlYXRlZCBhIGNvbmZpZGVuY2UgaW50ZXJ2YWwgdG8gcG9ydHJheSB0aGlzLg0KDQpOb3csIGFzIGJheWVzaWFucywgd2UgYXJlIGFsc28gaW5jb3Jwb3JhdGluZyBvdXIgcHJpb3IgYmVsaWVmcyBhcyB0byB3aGF0ICRcdGhldGEkIGlzIC0gdGhhdCBpdCBpcyBlaXRoZXIgZmFpciBvciBiaWFzZWQuIFdlIGJlbGlldmUgdGhhdCB0aGVyZSBpcyBub3QganVzdCByYW5kb20gZXJyb3IgYXNzb2NpYXRlZCBpbiBzYW1wbGluZyAtIHdlIGFsc28gdHJlYXQgJFx0aGV0YSQgYXMgYW4gYWRkaXRpb25hbCByYW5kb20gdmFyaWFibGUsIHNpbmNlIHdlIGRvbid0IGtub3cgd2hldGhlciBvdXIgY29pbiBpcyBmYWlyIG9yIGJpYXNlZCBlaXRoZXIsIGFuZCBmYWlyIGNvaW5zIHdpbGwgaGF2ZSBhIGRpZmZlcmVudCBzYW1wbGUgbWVhbiBkaXN0cmlidXRpb24gdGhhbiBiaWFzZWQgb25lcy4NCg0KYGBgIHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9N30NCmZhaXJfY29pbl9rX3Byb2IgPC0gYmlub21pYWxfcHJvYmFiaWxpdHkoaywgbiwgcCkNCmJpYXNlZF9jb2luX2tfcHJvYiA8LSBiaW5vbWlhbF9wcm9iYWJpbGl0eShrLCBuLCBrL24pDQpgYGANCg0KR2l2ZW4gdGhhdCB3ZSBvYnNlcnZlIGByIGtgIGhlYWRzLCB0aGUgbGlrZWxpaG9vZCBvZiB0aGlzIG9jY3VyaW5nIGlmIHRoZSBjb2luIGlzIGZhaXIgaXMgJGByIHBlcmNlbnQoZmFpcl9jb2luX2tfcHJvYilgJCUsIGFuZCAkYHIgcGVyY2VudChiaWFzZWRfY29pbl9rX3Byb2IpYCQlIGlmIHRoZSBjb2luIGlzIGJpYXNlZC4gSW4gdGhlIGFic2VuY2Ugb2YgaW5mb3JtYXRpdmUgcHJpb3IgaW5mb3JtYXRpb24gdGhlbiwgaXQgbWlnaHQgYXBwZWFyIHRoYXQgdGhlIGNvaW4gaXMgbW9yZSBsaWtlbHkgdG8gYmUgYmlhc2VkLg0KDQpIb3dldmVyLCB3ZSBhbHNvIG5lZWQgdG8gdGFrZSBpbnRvIGFjY291bnQgdGhhdCAkOTAkJSBvZiBjb2lucyBpbiB0aGUgcG9wdWxhdGlvbiBhcmUgZmFpciwgc28gZXZlbiBiZWZvcmUgb2JzZXJ2aW5nIGByIGtgIGhlYWRzLCB3ZSBoYXZlIHNvbWUgYmVsaWVmIGFib3V0IHdoZXRoZXIgdGhlIGNvaW4gaXMgZmFpciBvciBiaWFzZWQgKGkuZS4gdGhlIHByb2JhYmlsaXR5IGRpc3RyaWJ1dGlvbiBhc3NvY2lhdGVkIHdpdGggdGhlIHJhbmRvbSB2YXJpYWJsZSAkXHRoZXRhJCkuIA0KDQpgYGAge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03fQ0KZmFpcl9jb2luX3Byb2IgPC0gKGZhaXJfY29pbl9rX3Byb2IgKiAwLjkpLygoZmFpcl9jb2luX2tfcHJvYiAqIDAuOSkgKyAoYmlhc2VkX2NvaW5fa19wcm9iICogMC4xKSkNCmJpYXNlZF9jb2luX3Byb2IgPC0gKGJpYXNlZF9jb2luX2tfcHJvYiAqIDAuMSkvKChmYWlyX2NvaW5fa19wcm9iICogMC45KSArIChiaWFzZWRfY29pbl9rX3Byb2IgKiAwLjEpKQ0KDQojIGRhdGEuZnJhbWUoDQojIG1hdHJpeChkYXRhID0gYyhiaW5vbWlhbF9wcm9iYWJpbGl0eSgyLDEwLDAuNSkqMC45ICMgcChmYWlyLCAyIGhlYWRzKQ0KIyAgICAgICAgICAgICAgICAgLCAoMS1iaW5vbWlhbF9wcm9iYWJpbGl0eSgyLDEwLDAuNSkpKjAuOSAjIHAoZmFpciwgbm90IDIgaGVhZHMpDQojICAgICAgICAgICAgICAgICAsIDAuOSAjIHAoZmFpcikNCiMgICAgICAgICAgICAgICAgICwgYmlub21pYWxfcHJvYmFiaWxpdHkoMiwxMCwwLjIpKjAuMSAjIGJpYXNlZCwgMiBoZWFkcw0KIyAgICAgICAgICAgICAgICAgLCAoMS1iaW5vbWlhbF9wcm9iYWJpbGl0eSgyLDEwLDAuMikpKjAuMSAjIGJpYXNlZCwgbm90IDIgaGVhZHMNCiMgICAgICAgICAgICAgICAgICwgMC4xDQojICAgICAgICAgICAgICAgICAsIGJpbm9taWFsX3Byb2JhYmlsaXR5KDIsMTAsMC41KSowLjkgKyBiaW5vbWlhbF9wcm9iYWJpbGl0eSgyLDEwLDAuMikqMC4xDQojICAgICAgICAgICAgICAgICAsICgxLWJpbm9taWFsX3Byb2JhYmlsaXR5KDIsMTAsMC41KSkqMC45ICsgKDEtYmlub21pYWxfcHJvYmFiaWxpdHkoMiwxMCwwLjIpKSowLjENCiMgICAgICAgICAgICAgICAgICwgMQ0KIyAgICAgICAgICAgICAgICAgKQ0KIyAgICAgICAgLCBucm93ID0gMw0KIyAgICAgICAgLCBkaW1uYW1lcyA9IGxpc3QoYygnRmxpcCAyIGhlYWRzJywgJ05vdCBmbGlwIDIgaGVhZHMnLCAnVG90YWwnKQ0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgLCBjKCdGYWlyJywgJ0JpYXNlZCcsICdUb3RhbCcpKQ0KIyAgICAgICAgKQ0KIyApDQpgYGANCg0KLi5hbmQgc28gd2UgY2FuIGZpbmQgdGhlIGxpa2VsaWhvb2Qgd2UgZ290IGEgZmFpciBjb2luLCBnaXZlbiB0aGUgZmFjdCB3ZSBvYnNlcnZlZCAyIGhlYWRzOg0KDQokJA0KUChcdGV4dHtmYWlyfXxIPWByIGtgKSANCj1cZnJhY3tQKFx0ZXh0e0g9YHIga2AgYW5kIGZhaXJ9KX17UChcdGV4dHtIPWByIGtgIGFuZCBmYWlyfSkrUChcdGV4dHtIPWByIGtgIGFuZCBiaWFzZWR9KX0NCj1cZnJhY3tQKFx0ZXh0e0g9YHIga2B8ZmFpcn0pIFx0aW1lcyBQKFx0ZXh0e2ZhaXJ9KX17UChcdGV4dHtIPWByIGtgfSl9DQokJA0KDQokJA0KUChcdGV4dHtmYWlyfXxIPWByIGtgKQ0KPVxmcmFje2ByIHJvdW5kKGZhaXJfY29pbl9rX3Byb2IsNClgIFx0aW1lcyAwLjl9e2ByIHJvdW5kKGZhaXJfY29pbl9rX3Byb2IsNClgICsgYHIgcm91bmQoYmlhc2VkX2NvaW5fa19wcm9iLDQpYH09YHIgcm91bmQoZmFpcl9jb2luX3Byb2IsIDQpYA0KJCQNCg0KJCQNClAoXHRleHR7Ymlhc2VkfXxIPWByIGtgKSANCj1cZnJhY3tQKFx0ZXh0e0g9YHIga2AgYW5kIGJpYXNlZH0pfXtQKFx0ZXh0e0g9YHIga2AgYW5kIGZhaXJ9KStQKFx0ZXh0e0g9YHIga2AgYW5kIGJpYXNlZH0pfQ0KPVxmcmFje1AoXHRleHR7SD1gciBrYHxiaWFzZWR9KSBcdGltZXMgIFAoXHRleHR7Ymlhc2VkfSl9e1AoXHRleHR7SD1gciBrYH0pfQ0KJCQNCiQkDQpQKFx0ZXh0e2JpYXNlZH18SD1gciBrYCkgDQo9XGZyYWN7YHIgcm91bmQoYmlhc2VkX2NvaW5fa19wcm9iLDQpYCBcdGltZXMgMC4xfXtgciByb3VuZChmYWlyX2NvaW5fa19wcm9iLDQpYCArIGByIHJvdW5kKGJpYXNlZF9jb2luX2tfcHJvYiw0KWB9PWByIHJvdW5kKGJpYXNlZF9jb2luX3Byb2IsIDQpYA0KJCQNCg0KSW4gb3RoZXIgd29yZHMsIG91ciBwcmlvciBiZWxpZWYgKGJlZm9yZSBzYW1wbGluZyBhbmQgb2JzZXJ2aW5nIGFueSBkYXRhKSB3YXMgdGhhdCB0aGUgbGlrZWxpaG9vZCBvZiBvdXIgY29pbiBoYXZpbmcgJFx0aGV0YSQgZXF1YWwgdG8gJGByIHBgJCB3YXMgJDkwXCUkLCBhbmQgdGhlIGxpa2VsaWhvb2Qgb2YgaXQgaGF2aW5nIHRoZXRhIGVxdWFsIHRvICRgciBrL25gJCB3YXMgJDEwXCUkLiBBZnRlciBvYnNlcnZpbmcgYHIga2AgaGVhZHMsIHdlIHVwZGF0ZSBvdXIgYmVsaWVmcywgc28gdGhhdCB0aGUgbGlrZWxpaG9vZCBvZiB0aGUgcHJvYmFiaWxpdHkgb2Ygb3VyIGNvaW4gZmxpcHBpbmcgaGVhZHMgKCRcdGhldGEkKSBiZWluZyBlcXVhbCB0byAkMC41JCBpcyBub3cgZXF1YWwgdG8gJGByIHJvdW5kKGZhaXJfY29pbl9wcm9iKjEwMCwyKWBcJSQsIGFuZCBvZiBpdCBiZWluZyBlcXVhbCB0byAkYHIgc2FtcGxlX21lYW5gJCBiZWluZyAkYHIgcm91bmQoYmlhc2VkX2NvaW5fcHJvYioxMDAsMilgXCUkDQoNCkl0IGlzIHRoaXMgaW5jb3Jwb3JhdGlvbiBvZiBwcmlvciBkYXRhIHRoYXQgaXMgZWl0aGVyIHNlZW4gYXMgQmF5ZXNpYW4ncyBiaWdnZXN0IGFkdmFudGFnZSBvciBwaXRmYWxsLiBPbiB0aGUgb25lIGhhbmQsIGV4cGVyaW1lbnRzIGFyZSBub3QgYWJzdHJhY3QgZGV2aWNlcywgYW5kIHNvbWUga25vd2xlZGdlIGFib3V0IHRoZSBwcm9jZXNzIGJlaW5nIGludmVzdGlnYXRlZCBiZWZvcmUgb2J0YWluaW5nIHRoZSBkYXRhIGlzIGtub3duIGFuZCBhcmd1YWJseSBzaG91bGQgYmUgaW5jb3Jwb3JhdGVkLiBPbiB0aGUgb3RoZXIsIGluY29ycG9yYXRpbmcgc3ViamVjdGl2ZSBvcGluaW9ucywgcGFydGljdWxhcmx5IHN0cm9uZyBvbmVzLCBtYXkgbWVhbiB0aGF0IHlvdSBkbyBub3QgbGVhcm4gdGhlIHRydWUgdmFsdWVzIHlvdSBhcmUgdHJ5aW5nIHRvIGRlcml2ZS4gQmF5ZXNpYW4gaXMgdGh1cyBhbmFseXNpcyB0aGF0IHVzZXMgYSBzZXQgb2Ygb2JzZXJ2YXRpb25zIHRvIGNoYW5nZSBvcGluaW9uIHJhdGhlciB0aGFuIGFzIGEgbWVhbnMgdG8gZGV0ZXJtaW5lIHVsdGltYXRlIHRydXRoLg0KDQpUbyBoZWxwIGRlc2NyaWJlIGJheWVzaWFuIGFuYWx5c2lzLCB0aGUgZm9sbG93aW5nIHRlcm1zIGFyZSBvZnRlbiB1c2VkOg0KDQoqIFByaW9yIGRpc3RyaWJ1dGlvbjogJFByKFx0aGV0YSkkIC0gcmVwcmVzZW50cyBleGlzdGluZyBiZWxpZWYgYWJvdXQgJFx0aGV0YSQgKCpSZXByZXNlbnRzIHdoYXQgd2FzIHRob3VnaHQgYmVmb3JlIHNlZWluZyB0aGUgZGF0YSopLiBGb3IgZXhhbXBsZSwgaW4gdGhlIGJpbm9taWFsIGNvaW4gZXhhbXBsZSBhYm92ZSwgd2UgaGF2ZSBwcmlvciBrbm93bGVkZ2UgdGhhdCA5MCUgb2YgY29pbnMgaW4gb3VyIGVjb25vbXkgYXJlIGZhaXIsIHNvICRQKFx0aGV0YSk9UChCaWFzZWQpPTAuMSQNCiogRXZpZGVuY2U6ICRYJCAtIHdoYXQgd2UganVzdCBvYnNlcnZlZCAoJGByIGtgJCBoZWFkcykNCiogTWFyZ2luYWwgcHJvYmFiaWxpdHk6ICRQcihYKSQgLSB0aGUgdG90YWwgcHJvYmFiaWxpdHkgb2YgdGhlIGRhdGEgYWNyb3NzIGFsbCBwb3NzaWJsZSB2YWx1ZXMgb2YgdGhlIHBhcmFtZXRlciAkXHRoZXRhJC4gSXQgZG9lc24ndCBhY3R1YWxseSBkZXBlbmQgb24gJFx0aGV0YSQgYW5kIGlzb2Z0ZW4gcmVmZXJyZWQgdG8gYXMgdGhlIHByb3BvcnRpb25hbGl0eSBmYWN0b3Ivbm9ybWFsaXNpbmcgY29uc3RhbnQgKGl0IG1ha2VzIHN1cmUgYWxsIHRoZSBzY2VuYXJpb3MgYXJlIG1vZGVsbGVkLiBGb3IgZXhhbXBsZSwgJFAoSD1gciBrYCkgPSBQKEg9YHIga2B8RmFpcikgXHRpbWVzIFAoRmFpcikgKyBQKEg9YHIga2B8Qmlhc2VkKSBcdGltZXMgUChCaWFzZWQpJCkNCiogTGlrZWxpaG9vZCBmdW5jdGlvbjogJFByKFh8XHRoZXRhKSQgLSB0aGUgcHJvYmFiaWxpdHkgb2YgJFx0aGV0YSQgZ2l2ZW4gdGhlIGRhdGEgIG9ic2VydmVkICgqUmVwcmVzZW50cyB0aGUgbmV3IGRhdGEgYXZhaWxhYmxlKikuIEZvciBleGFtcGxlLCAkUChIPWByIGtgfEJpYXNlZCk9YHIgYmlhc2VkX2NvaW5fa19wcm9iYCQNCiogSm9pbnQgcHJvYmFiaWxpdHkgZGVuc2l0eSBmdW5jdGlvbjogJFByKFgsXHRoZXRhKT1QcigoWHxcdGhldGEpLlByKFx0aGV0YSkkIC0gdXNlZCB0byBtb2RpZnkgcHJpb3IgYmVsaWVmcyB0aHJvdWdoIEJheWVzIFRoZW9yZW0NCiogUG9zdGVyaW9yIGRpc3RyaWJ1dGlvbjogJFByKFx0aGV0YXxYKSQgLSB0aGUgKnBvc3RlcmlvciBkZW5zaXR5KiByZXByZXNlbnRzIHRoZSBrbm93bGVkZ2UgYWJvdXQgdGhlIG1vZGVsIHBhcmFtZXRlcnMgYWZ0ZXIgb2JzZXJ2aW5nIHRoZSBkYXRhICgqUmVwcmVzZW50cyB3aGF0IGlzIG5vdyB0aG91Z2h0IGdpdmVuIGJvdGggcHJpb3IgZGF0YSBhbmQgZGF0YSBqdXN0IG9idGFpbmVkKikNCiogQ29uanVnYW5jeTogb2NjdXJzIHdoZW4gdGhlIHBvc3RlcmlvciBkaXN0cmlidXRpb24gaXMgaW4gdGhlIHNhbWUgZmFtaWx5IG9mIHByb2JhYmlsaXR5IGRlbnNpdHkgZnVuY3Rpb25zIGFzIHRoZSBwcmlvciBiZWxpZWYsIGJ1dCB3aXRoIG5ldyBwYXJhbWV0ZXIgdmFsdWVzICh1cGRhdGVkIHRvIHJlZmxlY3Qgd2hhdCBoYXMgYmVlbiBsZWFybmVkIGZyb20gdGhlIGRhdGEpLiBUaGUgcG9zdGVyaW9yIGNvbWVzIGZyb20gdGhlIHNhbWUgZmFtaWx5IGFzIHRoZSBwcmlvciwgcmF0aGVyIHRoYW4gdGhlIGRhdGEncyBkaXN0cmlidXRpb24uDQoNCkFuZCB0aHVzLCB3ZSBjYW4gdGhlbiBkZXNjcmliZSBCYXllcyBUaGVvcmVtOg0KDQokJA0KUChcdGhldGF8WCkgPVxmcmFje1AoWHxcdGhldGEpIFx0aW1lcyBQKFx0aGV0YSl9e1AoWHxcdGhldGEpIFx0aW1lcyBQKFx0aGV0YSkgKyBQKFh8XHRoZXRhJykgXHRpbWVzIFAoXHRoZXRhJyl9ID1cZnJhY3tQKFh8XHRoZXRhKSBcdGltZXMgUChcdGhldGEpfXtQKFgpfQ0KJCQNCg0KIyMgVGhpbmtpbmcgYWJvdXQgQmF5ZXNpYW4gaW4gdGVybXMgb2YgcHJlZGljdGluZyBnZXR0aW5nIGEgaGVhZCBuZXh0IHRpbWU6DQoNClRoZXJlIGFyZSB0d28gc291cmNlcyBvZiB1bmNlcnRhaW50eSB3aGVuIGJ1aWxkaW5nIHN0YXRpc3RpY2FsIG1vZGVscyB0byBwcmVkaWN0IHRoaW5nczoNCg0KKiBVbmNlcnRhaW50eSBkdWUgdG8gdGhlIGZhY3QgYW55IGZ1dHVyZSB2YWx1ZSBpcyBpdHNlbGYgYSByYW5kb20gZXZlbnQsICRQKHl8XHRoZXRhKSQgKGJlY2F1c2UgZnV0dXJlIGV2ZW50cyBhcmUgZXNzZW50aWFsbHkgc2FtcGxlcyB0aGF0IGhhdmUgZXJyb3IgY29uZGl0aW9uZWQgYnkgJFx0aGV0YSQpDQoqIFVuY2VydGFpbnR5IGluIHRoZSBwYXJhbWV0ZXIgdmFsdWVzIHdoaWNoIGhhdmUgYmVlbiBlc3RpbWF0ZWQgb24gdGhlIGJhc2lzIG9mIHBhc3QgZGF0YSwgJFAoXHRoZXRhfFgpJCAoZm9yIGV4YW1wbGUsIGEgY29lZmZpY2llbnQgZXN0aW1hdGVkIGluIGEgcmVncmVzc2lvbiBtb2RlbCkNCg0KQ2xhc3NpY2FsIG1vZGVscyBkZWFsIHdpdGggcG9pbnQgMSwgbWFraW5nIGEgcG9pbnQgZXN0aW1hdGUgaW4gdGhlIGZ1dHVyZSBiYXNlZCBvbiB0aGUgJ2Jlc3QnIHBhcmFtZXRlcnMgZXN0aW1hdGVkIGZyb20gcGFzdCBkYXRhLiBUaGlzIGlzIGVxdWl2YWxlbnQgdG8gZm9ybWluZyBhIGxpa2VsaWhvb2QgKGkuZS4gaW5jb3Jwb3JhdGluZyBuZXcgZGF0YSkuDQpXaGF0IHRoZXkgZG9uJ3QgZG8gdGhvdWdoIGlzIHRoaW5rIGFib3V0IHBvaW50IDIsIHRoYXQgdGhlIHNlZW1pbmdseSBvcHRpbXVtIG1vZGVsIGVzdGltYXRlZCBpdHNlbGYgbWlnaHQgYmUgaW5jb3JyZWN0IHNpbmNlIHRoZSB0cnVlIHZhbHVlcyBvZiAkXHRoZXRhJCBhcmUgbm90IGZpeGVkLCBhbmQgdGhpcyBpcyBlcXVpdmFsZW50IHRvIG5vdCBidWlsZGluZyBpbiBhbnkgcHJpb3IgYmVsaWVmcy4gSW4gYSB3YXksIGJ5IGdpdmluZyBhIHBvaW50IGVzdGltYXRlLCB0aGUgbW9kZWxzIGdlbmVyYXRlIGEgZmFsc2Ugc2Vuc2Ugb2YgcHJlY2lzaW9uLCBhbmQgdGhlIGNvbmZpZGVuY2UgaW50ZXJ2YWxzIGFyb3VuZHMgYW55IGVzdGltYXRlIGlzIG9ubHkgcHJlZGljYXRlZCBvbiB0aGUgaWRlYSB0aGVyZSBpcyBzYW1wbGluZyByYW5kb21uZXNzLCByYXRoZXIgdGhhbiByYW5kb21uZXNzIGFzc29jaWF0ZWQgd2l0aCAkXHRoZXRhJCBpdHNlbGYuDQoNCkluIG90aGVyIHdvcmRzLCBjbGFzc2ljYWwgbW9kZWxzIHNlZWsgdG8gYmVzdCBmaXQgdGhlIG5ldyBldmlkZW5jZSAodHJhZGl0aW9uYWxseSB0aHJvdWdoIG1heGltdW0gbGlrZWxpaG9vZCBlc3RpbWF0aW9uIGZvciBnZW5lcmFsaXplZCBsaW5lYXIgbW9kZWxzKSwgd2hlcmVhcyBiYXllc2lhbiBtb2RlbHMgaW5jb3Jwb3JhdGUgcHJpb3IgYmVsaWVmcyBhcyB3ZWxsLg0KDQpUbyBwcmVkaWN0IHRoZSBsaWtlbGlob29kIG9mIGdldHRpbmcgaGVhZHMgbmV4dCB0aW1lLCBnaXZlbiBvdXIgZXZpZGVuY2UsIHdlIGRvIHRoZSBmb2xsb3dpbmc6DQoNCiQkDQpcYmVnaW57YWxpZ25lZH0NClAoXHRleHR7aGVhZHN9fGs9MikNClxcID0gUChcdGV4dHtoZWFkc318XHRleHR7ZmFpcn0saz1gciBrYCkgXHRpbWVzIFAoXHRleHR7ZmFpcn18az1gciBrYCkgKyBQKFx0ZXh0e2hlYWRzfXxcdGV4dHtiaWFzZWR9LGs9YHIga2ApIFx0aW1lcyBQKFx0ZXh0e2JpYXNlZH18az1gciBrYCkNClxcID0gMC41IFx0aW1lcyBQKFx0ZXh0e2ZhaXJ9fGs9YHIga2ApICsgYHIgay9uYCBcdGltZXMgUChcdGV4dHtiaWFzZWR9fGs9YHIga2ApDQpcXCA9IDAuNSBcdGltZXMgXGZyYWN7UChrPWByIGtgfFx0ZXh0e2ZhaXJ9KSBcdGltZXMgUChcdGV4dHtmYWlyfSl9e1Aoaz1gciBrYCl9ICsgYHIgay9uYCBcdGltZXMgXGZyYWN7UChrPWByIGtgfFx0ZXh0e2JpYXNlZH0pIFx0aW1lcyBQKFx0ZXh0e2JpYXNlZH0pfXtQKGs9YHIga2ApfQ0KXFwgPSAwLjUgXHRpbWVzIGByIHJvdW5kKGZhaXJfY29pbl9wcm9iLCA0KWAgKyBgciBrL25gIFx0aW1lcyBgciByb3VuZChiaWFzZWRfY29pbl9wcm9iLDQpYA0KXFwgPSBgciByb3VuZCgwLjUqZmFpcl9jb2luX3Byb2IrMC4yKmJpYXNlZF9jb2luX3Byb2IsNClgDQpcZW5ke2FsaWduZWR9DQokJA0KDQpOb3RlIHRoYXQgd2UgaGF2ZSBzdWJiZWQgaW4gb3VyIHJlc3VsdHMgZm9yIHRoZSBwcm9iYWJpbGl0eSB0aGF0IHdlIGhhdmUgYSBmYWlyIG9yIGEgYmlhc2VkIGNvaW4gdXNpbmcgYmF5ZXMgdGhlb3JlbSwgdG8gYWxsb3cgdXMgdG8gcHJlZGljdCB0aGUgcHJvYmFiaWxpdHkgd2UgZ2V0IGhlYWRzIG5leHQgdGltZS4NCg0KSWYgd2UgY2hvc2UgYSBwdXJlbHkgZnJlcXVlbnRpc3QgYXBwcm9hY2gsIHdlIHdvdWxkIGhhdmUgZ3Vlc3NlZCB0aGF0IHRoZSBsaWtlbGlob29kIGlzICRgciBrL25gJCwgc2luY2Ugd2Ugb2JzZXJ2ZWQgJGByIGtgJCBoZWFkcywgYW5kIFx0aGV0YSBjYW4gb25seSB0YWtlIG9uZSB2YWx1ZSAoYW5kIHRoaXMgd2FzIHRoZSBtYXhpbXVtIGxpa2VsaWhvb2QgZXN0aW1hdGUpLiBJZiB3ZSBjaG9zZSB0byBvbmx5IHVzZSBvdXIgcHJpb3IgaW5mb3JtdGlvbiB0aGF0IDkwJSBvZiBjb2lucyB3ZXJlIGZhaXIsIHdlIHdvdWxkIGhhdmUgZ3Vlc3NlZCAkMC45IFx0aW1lcyAwLjUgKyAwLjEgXHRpbWVzIGByIGsvbmAgPSBgciAwLjkqMC41KzAuMSprL25gJC4gSG93ZXZlciwgc2luY2Ugd2UgaGF2ZSBpbmNvcnBvcmF0ZWQgYm90aCB0aGUgcHJpb3IgaW5mb3JtYXRpb24gYW5kIHRoZSBuZXcgZXZpZGVuY2UsIHdlIHByZWRpY3QgYSByZXN1bHQgdGhhdCBpcyBzb21ld2hlcmUgaW4gdGhlIG1pZGRsZSwgYXQgJGByIHJvdW5kKDAuNSpmYWlyX2NvaW5fcHJvYiswLjIqYmlhc2VkX2NvaW5fcHJvYiw0KWAkLg0KDQpXZSBjYW4gZm9ybWFsaXNlIHRoaXM6DQoNCiQkDQpmKHl8eCk9IFxpbnQgZih5fFx0aGV0YSx4KWYoXHRoZXRhfHgpIFxwYXJ0aWFsKFx0aGV0YSkNCiQkDQpJbiBvdGhlciB3b3JkcywgZm9yIGFsbCBvdXIgcG90ZW50aWFsIGVzdGltYXRlcyBvZiB3aGF0ICRcdGhldGEkIGNvdWxkIGJlICgkXGRlbHRhKFx0aGV0YSkkKSwgaG93IGxpa2VseSBlYWNoIG9mIHRob3NlIHZhbHVlcyBmb3IgJFx0aGV0YSQgbWlnaHQgYmUgZ2l2ZW4gdGhlIG51bWJlciBvZiBoZWFkcyB3ZSBoYXZlIG9ic2VydmVkICgkZihYfFx0aGV0YSkkKSwgYW5kIGhvdyBsaWtlbHkgd2Ugd2VyZSB0byBvYnNlcnZlIG91ciBudW1iZXIgb2YgaGVhZHMgZ2l2ZW4gdGhvc2UgcGFyYW1ldGVycyAkUCh5fFx0aGV0YSx4KSQsIHdlIGNhbiBtYWtlIGEgcHJlZGljdGlvbiBmb3Igd2hhdCBvdXIgbnVtYmVyIG9mIGhlYWRzIG1pZ2h0IGJlIGluIHRoZSBuZXh0IGZsaXAuDQoNClRoaXMgbm93IGFsbG93cyB1cyB0byB0YWNrbGUgcHJvYmxlbXMgd2hlcmUgdGhlIHByaW9yIGNvbWVzIGZyb20gY29udGludW91cyBkaXN0cmlidXRpb25zLg0KDQojIyBDb250aW51b3VzIHByaW9yIGJlbGllZnMNCg0KSW4gdGhlIGxhc3QgZXhhbXBsZSwgd2UgaGFkIGEgdmVyeSBzdHJvbmcgcHJpb3IgLSAkXHRoZXRhJCBjb3VsZCBvbmx5IHRha2UgMiB2YWx1ZXMsIGFuZCB3YXMgOTAlIGxpa2VseSB0byBiZSBmYWlyLCBzbyBpdCBoYWQgYSB2ZXJ5IGxhcmdlIGluZmx1ZW5jZSBvbiB0aGUgcmVzdWx0LiBIb3dldmVyLCBCYXllc2lhbiB0ZW5kcyB0byBtb3N0IHZhbHVhYmxlIHdoZXJlIGJvdGggdGhlIG5ldyBkYXRhIGFuZCBwYXN0IHByaW9yIGhhdmUgbW9yZSBlcXVhbCBpbmZsdWVuY2UsIGFuZCB0aGUgcG9zdGVyaW9yIGZvcm1pbmcgYSBkaXN0cmlidXRpb24gdGhhdCBzaXRzIHNvbWV3aGVyZSBiZXR3ZWVuIHRoYXQgZXN0aW1hdGVkIGJ5IHRoZSBwcmlvciBhbmQgbGlrZWxpaG9vZCBmdW5jdGlvbi4NCg0KU28gLSBsZXQncyBzYXkgdGhlIGxhc3QgdGltZSB3ZSB0cmllZCBmbGlwcGluZyBgciBuYCB0aW1lcywgd2UgZ290IGhlYWRzIGByIHAqbmAgdGltZXMgKG91ciBwcmlvcikgYW5kIHRoZSBuZXcgY29pbiBmbGlwcGVkIGhlYWRzIGByIGtgIHRpbWVzIChvdXIgbmV3IGV2aWRlbmNlKS4gV2UgY2FuIHRoZW4gZ2V0IHRoZSBmb2xsb3dpbmcgZXF1YXRpb25zOg0KDQpgYGAge3IsIGVjaG89RkFMU0V9DQprMSA8LSA1DQpuMSA8LSAxMA0KazIgPC0gaw0KbjIgPC0gMTANCmBgYA0KDQpPdXIgcHJpb3IgaXMgYSBiaW5vbWlhbDoNCg0KJCQNClx0ZXh0e1ByaW9yOiB9IFAoXHRoZXRhKSBcc2ltIFx0ZXh0e0Jpbm9tfVtuX3sxfSxrX3sxfS9uXzFdID0gXHRleHR7Qmlub219W2ByIG4xYCxgciBrMS9uMWBdDQokJA0KT3VyIGxpa2VsaWhvb2QgaXMgYWxzbyBiaW5vbWlhbDoNCg0KJCQNClx0ZXh0e0xpa2VsaG9vZDogfSBQKFh8XHRoZXRhKSBcc2ltXHRleHR7Qmlub219W25fezJ9LGtfezJ9L25fMl0gPSBcdGV4dHtCaW5vbX1bYHIgbjJgLGByIGsyL24yYF0NCiQkDQpPdXIgcG9zdGVyaW9yIHRodXMgZm9sbG93cyBhIGJpbm9taWFsIGRpc3RyaWJ1dGlvbiB0b286DQoNCiQkDQpcdGV4dHtQb3N0ZXJpb3I6IH0gUChcdGhldGF8WCkgPSBcZnJhY3tQKFh8XHRoZXRhKVAoXHRoZXRhKX17XGludCBQKFh8XHRoZXRhKVAoXHRoZXRhKSBkXHRoZXRhfSAgXGFwcHJveCBcdGV4dHtCaW5vbX1cbGVmdFtuXzErbl8yLFxmcmFje2tfMStrXzJ9e25fMStuXzJ9XHJpZ2h0XSA9IFx0ZXh0e0Jpbm9tfVtgciBrMStrMmAsYHIgKGsxK2syKS8objErbjIpYF0gXFwNCiQkDQoNCkluIG90aGVyIHdvcmRzLCB3ZSBvYnRhaW4gb3VyIHBvc3RlcmlvciBlc3RpbWF0ZSBieSBtdWx0aXBseWluZyBvdXIgbGlrZWxpaG9vZCBmdW5jdGlvbiBhbmQgcHJpb3IgYmVsaWVmcywgYW5kIGRpdmlkaW5nIGl0IGJ5IGFsbCBwb3NzaWJsZSBwcm9iYWJpbGl0aWVzIGdpdmVuIG91ciBldmlkZW5jZSB0aGF0IHRoZXRhIGNvdWxkIHRha2UuIEluIHByYWN0aWNlLCB0aGlzIGRlbm9taW5hdG9yIGp1c3QgZW5zdXJlcyB0aGF0IG91ciBwcm9iYWJpbGl0aWVzIHN1bSB0byBvbmUuDQoNCldlIGNhbiBwbG90IHRoaXMgdG8gc2VlIHdoYXQgaXMgbG9va3MgbGlrZToNCg0KYGBgIHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9N30NCiMgcHJpb3IgPC0gc2FwcGx5KHgsIEZVTiA9IGZ1bmN0aW9uKHRoZXRhKSB7YmV0YV9wcm9iYWJpbGl0eSh0aGV0YSxrMSsxLG4xLWsxKzEpLyhuMSsxKX0pDQojIGxpa2VsaWhvb2RfZiA8LSBzYXBwbHkoeCwgRlVOID0gZnVuY3Rpb24odGhldGEpIHtiZXRhX3Byb2JhYmlsaXR5KHRoZXRhLGsyKzEsbjItazIrMSkvKG4yKzEpfSkNCiMgcG9zdGVyaW9yIDwtIHNhcHBseSh4LCBGVU4gPSBmdW5jdGlvbih0aGV0YSkge2JldGFfcHJvYmFiaWxpdHkodGhldGEsazErazIrMixuMS1rMSsxK24yLWsyKzEpLyhuMStuMisyKX0pDQoNCiMgcHJpb3IgPC0gZGJldGEoeCwgazErMSwgbjEtazErMSkvKG4xKzEpDQojIGxpa2VsaWhvb2RfZiA8LSBkYmV0YSh4LCBrMisxLCBuMi1rMisxKS8objIrMSkNCiMgcG9zdGVyaW9yIDwtIGRiZXRhKHgsIGsxK2syKzEsIG4xLWsxK24yLWsyKzEpLyhuMStuMisxKQ0KDQpwcmlvciA8LSBkYmlub20oc2VxKDAsMTAsMSksIG4xLCBrMS9uMSkNCmxpa2VsaWhvb2RfZiA8LSBkYmlub20oc2VxKDAsMTAsMSksIG4yLCBrMi9uMikNCnBvc3RlcmlvciA8LSBkYmlub20oc2VxKDAsMjAsMiksIG4xK24yLCAoazErazIpLyhuMStuMikpKjINCg0KcGxvdF9seSh0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ2xpbmVzK21hcmtlcnMnKSAlPiUNCiAgYWRkX3RyYWNlKHggPSBzZXEoMCwxMCwxKSwgeSA9IHByaW9yLCBuYW1lID0gJ1ByaW9yIGJlbGllZjogSG9yc2UnKSAlPiUNCiAgYWRkX3RyYWNlKHggPSBzZXEoMCwxMCwxKSwgeSA9IGxpa2VsaWhvb2RfZiwgbmFtZSA9ICdMaWtlbGlob29kIChOZXcgZGF0YSk6IERvbmtleScpICU+JQ0KICBhZGRfdHJhY2UoeCA9IHNlcSgwLDEwLDEpLCB5ID0gcG9zdGVyaW9yLCBuYW1lID0gJ1Bvc3RlcmlvciBCZWxpZWY6IE11bGUnKSAlPiUNCiAgbGF5b3V0KHRpdGxlID0gIlVwZGF0aW5nIFByaW9yIEJlbGllZnMiKQ0KYGBgDQoNCg0KVGhlIHBvc3RlcmlvciBjdXJ2ZSBpcyBib3RoIHRhbGxlciBhbmQgbmFycm93IHRoYW4gYm90aCB0aGUgZGlzdHJpYnV0aW9ucyBlc3RpbWF0ZWQgZnJvbSB0aGUgcHJpb3IgYW5kIHRoZSBsaWtlbGlob29kIGZ1bmN0aW9ucy4gVGhpcyBpcyBiZWNhdXNlLCBieSBpbmNsdWRpbmcgYm90aCBwcmlvciBkYXRhIGFuZCBuZXcgZXZpZGVuY2UsIG91ciBzYW1wbGUgc2l6ZSBoYXMgaW5jcmVhc2VkLCBzbyB0aGlzIHJlZmxlY3RzIGdyZWF0ZXIgY29uZmlkZW5jZSB0aGF0IHRoZSBvYnNlcnZlZCBudW1iZXIgb2YgaGVhZHMsY29uZGl0aW9uZWQgYnkgdGhlIHByb2JhYmlsaXR5IGRpc3RyaWJ1dGlvbiBvZiAkXHRoZXRhJCwgd2lsbCBsaWUgd2l0aGluIGEgc21hbGxlciBpbnRlcnZhbC4NCg0KSG93ZXZlciwgaW4gZ2VuZXJhbCwgd2UgbWlnaHQgaGF2ZSBhIGNvbnRpbnVvdXMgcHJpb3Igb3IgbGlrZWxpaG9vZCBmdW5jdGlvbiwgYW5kIHRoZSBiZXRhIGRpc3RyaWJ1dGlvbiBpcyBhIGNvbnRpbnVvdXMgZnVuY3Rpb25hbCBmb3JtIG9mIHRoZSBiaW5vbWlhbCBkaXN0cmlidXRpb24uDQoNCipBIHF1aWNrIGludHJvZHVjdGlvbiB0byB0aGUgYmV0YSBkaXN0cmlidXRpb246IGl0IGNhbiBtb2RlbCBsb3RzIG9mIGRpZmZlcmVudCBmdW5jdGlvbmFsIGZvcm1zIHVzaW5nICRhJCBhbmQgJGIkIGFzIHBhcmFtZXRlcnMsIGJ5IGlucHV0dGluZyB0aGVtIGludG8gdGhlIGZvbGxvd2luZyBmdW5jdGlvbjoqDQoNCiQkDQpcdGV4dHtCZXRhfVthLGJdIFxzaW0gXHRoZXRhXnthLTF9XHRpbWVzKDEtXHRoZXRhKV57Yi0xfQ0KJCQNCg0KTm93IGxldCdzIGRlZmluZSB0aGUgYmlub21pYWwgZGlzdHJpYnV0aW9uIGZvciBjb21wYXJpc29uOg0KDQokJA0KXHRleHR7Qmlub219KG4scCkgPSB7biBcY2hvb3NlIGsgfSBbXHRoZXRhXntrfVx0aW1lcygxLVx0aGV0YSlee24ta31dICANCiQkDQoNCk5vdyB0byBnZXQgZ2Vla3kgLSB5b3UgbWlnaHQgaGF2ZSBhbHJlYWR5IG5vdGljZWQgaG93IGVlcmlseSBzaW1pbGFyIHRoZSBiZXRhIGRpc3RyaWJ1dGlvbiBpcyB0byB0aGUgYmlub21pYWwgZGlzdHJpYnV0aW9uIGZyb20gdGhlIGZvcm11bGEgLSBhbmQgaW4gZmFjdCB5b3UgY2FuIHVzZSB0aGUgYmV0YSBkaXN0cmlidXRpb24gYXMgYSBjb250aW5vdXMgZnVuY3Rpb24gZXF1aXZhbGVudCB0byB0aGUgYmlub21pYWwgZnVuY3Rpb24uIA0KDQokJA0KcChcdGV4dHtIPX1rfCBcdGhldGEpIFxzaW0gXHRoZXRhXmtcdGltZXMoMS1cdGhldGEpXntuLWt9IFxhcHByb3ggXHRleHR7QmV0YX1baysxLG4taysxXQ0KJCQNCg0KQm90aCBiaW5vbWlhbCBhbmQgYmV0YSBsaWtlbGlob29kcyBhcmUgc2NhbGVkIHRob3VnaCAobm9ybWFsaXplZCksIHNvIHRoYXQgdGhlIHN1bSBvZiB0aGVpciBwcm9iYWJpbGl0ZXMgdG90YWwgMS4NCkZvciB0aGUgYmV0YSBkaXN0cmlidXRpb24sIHRoZSBzY2FsaW5nIGlzIGFjaGlldmVkIGxpa2UgdGhpczoNCg0KJCQNCkJldGFbXGFscGhhLFxiZXRhXSA9IFx0aGV0YV57XGFscGhhLTF9XHRpbWVzKDEtXHRoZXRhKV57XGJldGEtMX0gXHRpbWVzIFxmcmFjezF9e1x0ZXh0e0J9KFxhbHBoYSxcYmV0YSl9DQokJA0KV2hlcmUNCg0KJCQNClxmcmFjezF9e1x0ZXh0e0J9KFxhbHBoYSxcYmV0YSl9ID0gXGZyYWN7XEdhbW1hKFxhbHBoYStcYmV0YSl9e1xHYW1tYShcYWxwaGEpIFx0aW1lcyBcR2FtbWEoXGJldGEpfSA9IFxmcmFjeyhcYWxwaGErXGJldGEtMSkhfXsoXGFscGhhLTEpISBcdGltZXMgKFxiZXRhLTEpIX0NCiQkDQoNClNvIGlmIHN1Y2Nlc3NlcyAkXGFscGhhID0gaysxJCBhbmQgZmFpbHVyZXMgJFxiZXRhID0gbiAtIGsgKyAxJCwgdGhlbjoNCg0KJCQNClxmcmFjeyhcYWxwaGErXGJldGEtMSkhfXsoXGFscGhhLTEpISBcdGltZXMgKFxiZXRhLTEpIX09XGZyYWN7KChrKzEpKyhuLWsrMSktMSkhfXsoKGsrMSktMSkhIFx0aW1lcyAoKG4taysxKS0xKSF9PVxmcmFjeyhuKzEpIX17KGspIVx0aW1lcyhuLWspIX0gPQ0Ke24rMSBcY2hvb3NlIGsgfSA9IChuKzEpIFx0aW1lcyB7biBcY2hvb3NlIGsgfQ0KJCQNCg0KDQoNCg0KQW5kIHRodXM6DQoNCiQkDQpCZXRhW2srMSxuLWsrMV0gPSAobisxKSBcdGltZXMgXHRleHR7Qmlub219KG4scCkNCiQkDQoNClRoaXMgcmVsYXRpb25zaGlwIGhlbHBzIHVzIGNvbWJpbmUgYmlub21pYWwgbGlrZWxpaG9vZHMgYW5kIGJldGEgcHJpb3JzIHRvIGNyZWF0ZSBhIHBvc3RlcmlvciBpbiB3aGF0IGlzIGNhbGxlZCAqKmNvbmp1Z2FuY3kqKi4gQ29uanVnYW5jeSBvY2N1cnMgd2hlbiB0aGUgcG9zdGVyaW9yIGRpc3RyaWJ1dGlvbiBpcyBpbiB0aGUgc2FtZSBmYW1pbHkgb2YgcHJvYmFiaWxpdHkgZGVuc2l0eSBmdW5jdGlvbnMgYXMgdGhlIHByaW9yIGJlbGllZiwgYnV0IHdpdGggbmV3IHBhcmFtZXRlciB2YWx1ZXMgKHVwZGF0ZWQgdG8gcmVmbGVjdCB3aGF0IGhhcyBiZWVuIGxlYXJuZWQgZnJvbSB0aGUgZGF0YSkuIFRoZSBwb3N0ZXJpb3IgY29tZXMgZnJvbSB0aGUgc2FtZSBmYW1pbHkgYXMgdGhlIHByaW9yLCByYXRoZXIgdGhhbiB0aGUgZGF0YSdzIGRpc3RyaWJ1dGlvbi4NCg0KU28gLSBpZiB3ZSB3YW50IHRvIHVzZSBhIGJldGEgZGlzdHJpYnV0aW9uIGZvciBvdXIgYmlub21pYWwgcHJpb3IsIHdlIGNhbiBtb2RlbCB0aGlzIHVzaW5nIGEgYmV0YSBkaXN0cmlidXRpb24gYnkgc2V0dGluZyAkYSQgYW5kICRiJCB0byBib3RoIGVxdWFsIHRvIDENCg0KJCQNClx0ZXh0e1ByaW9yOiB9IHAoXHRoZXRhKSBcYXBwcm94IFx0ZXh0e0JldGF9W2tfezF9KzEsbl97MX0ta197MX0rMV0gPSBcdGV4dHtCZXRhfVtgciBrMSsxYCxgciBuMWAtYHIgazFgKzFdID0gXHRleHR7QmV0YX1bYHIgazFgLGByIG4xLWsxKzFgXQ0KJCQNCg0KJCQNClx0ZXh0e0xpa2VsaWhvb2Q6IH0gcChrfFx0aGV0YSk9XHRleHR7Qmlub219W25fezJ9LGtfezJ9L25fMl0gPSBcdGV4dHtCZXRhfVtrX3syfSsxLG5fezJ9LWtfezJ9KzFdID0gXHRleHR7QmV0YX1bYHIgazJgKzEsYHIgbjJgLWByIGsyYCsxXSA9IFx0ZXh0e0JldGF9W2ByIGsyKzFgLGByIG4yLWsyKzFgXQ0KJCQNCg0KJCQNClx0ZXh0e1Bvc3RlcmlvcjogfSBwKFx0aGV0YXxrKSBcYXBwcm94IFxcDQpcdGV4dHtCZXRhfVtgciBrMWAsYHIgbjEtazErMWBdIFx0aW1lcyBcdGV4dHtCZXRhfVtgciBrMisxYCxgciBuMi1rMisxYF0gPSBcXA0KXHRleHR7QmV0YX1ba18xK2tfezJ9KzIsbl8xLWtfMStuX3syfS1rX3syfSsyXSA9IFx0ZXh0e0JldGF9W2ByIGsxK2syKzJgLGByIG4xLWsxK24yLWsyKzJgXQ0KJCQNCg0KDQpgYGAge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03fQ0KIyBwcmlvciA8LSBzYXBwbHkoeCwgRlVOID0gZnVuY3Rpb24odGhldGEpIHtiZXRhX3Byb2JhYmlsaXR5KHRoZXRhLGsxKzEsbjEtazErMSkvKG4xKzEpfSkNCiMgbGlrZWxpaG9vZF9mIDwtIHNhcHBseSh4LCBGVU4gPSBmdW5jdGlvbih0aGV0YSkge2JldGFfcHJvYmFiaWxpdHkodGhldGEsazIrMSxuMi1rMisxKS8objIrMSl9KQ0KIyBwb3N0ZXJpb3IgPC0gc2FwcGx5KHgsIEZVTiA9IGZ1bmN0aW9uKHRoZXRhKSB7YmV0YV9wcm9iYWJpbGl0eSh0aGV0YSxrMStrMisyLG4xLWsxKzErbjItazIrMSkvKG4xK24yKzIpfSkNCg0KIyBwcmlvciA8LSBkYmV0YSh4LCBrMSsxLCBuMS1rMSsxKS8objErMSkNCiMgbGlrZWxpaG9vZF9mIDwtIGRiZXRhKHgsIGsyKzEsIG4yLWsyKzEpLyhuMisxKQ0KIyBwb3N0ZXJpb3IgPC0gZGJldGEoeCwgazErazIrMSwgbjEtazErbjItazIrMSkvKG4xK24yKzEpDQoNCnggPC0gc2VxKDAsIDEsIGxlbmd0aD0xMDArMSkNCg0KcHJpb3IgPC0gZGJldGEoeCwgazErMSwgbjEtazErMSkjLyhuMSsxKQ0KbGlrZWxpaG9vZF9mIDwtIGRiZXRhKHgsIGsyKzEsIG4yLWsyKzEpIy8objIrMSkNCnBvc3RlcmlvciA8LSBkYmV0YSh4LCBrMStrMisyLCBuMS1rMStuMi1rMisyKSMvKChuMSsxKSoobjIrMSkpDQojIHByaW9yIDwtIHByaW9yL3N1bShwcmlvcikNCiMgbGlrZWxpaG9vZF9mIDwtIGxpa2VsaWhvb2RfZi9zdW0obGlrZWxpaG9vZF9mKQ0KIyBwb3N0ZXJpb3IgPC0gcG9zdGVyaW9yL3N1bShwb3N0ZXJpb3IpDQoNCnBsb3RfbHkodHlwZSA9ICdzY2F0dGVyJywgbW9kZSA9ICdsaW5lcycpICU+JQ0KICBhZGRfdHJhY2UoeCA9IHgsIHkgPSBwcmlvciwgbmFtZSA9ICdQcmlvciBiZWxpZWY6IEhvcnNlJykgJT4lDQogIGFkZF90cmFjZSh4ID0geCwgeSA9IGxpa2VsaWhvb2RfZiwgbmFtZSA9ICdMaWtlbGlob29kIChOZXcgZGF0YSk6IERvbmtleScpICU+JQ0KICBhZGRfdHJhY2UoeCA9IHgsIHkgPSBwb3N0ZXJpb3IsIG5hbWUgPSAnUG9zdGVyaW9yIEJlbGllZjogTXVsZScpICU+JQ0KICBsYXlvdXQodGl0bGUgPSAiVXBkYXRpbmcgUHJpb3IgQmVsaWVmcyIpDQpgYGANCg0KV2h5IG1pZ2h0IHdlIHdhbnQgdG8gdXNlIGJldGEgdGhvdWdoIHJhdGhlciB0aGFuIGp1c3QgYmlub21pYWwgYXMgb3VyIHByaW9yPyBXZWxsIGEgYmV0YSBkaXN0cmlidXRpb24gaXMgcmVhbGx5IGZsZXhpYmxlLCBzbyB3ZSBjYW4gdXNlIGl0IHRvIG1vZGVsIGEgcmFuZ2Ugb2YgZGlmZmVyZW50IHByaW9ycyBkZXBlbmRpbmcgb24gcHJldmlvdXMgZXhwZXJpbWVudHMsIG9yIGV2ZW4gYXMgYSB3YXkgb2YgcmVndWxhcmlzaW5nIHRocm91Z2ggdGhlIGJheWVzaWFuIHBhcmFkaWdtLg0KDQpTbyAtIGlmIHdlIHdhbnQgdG8gY3JlYXRlIGEgdW5pZm9ybSBkaXN0cmlidXRpb24gZm9yIG91ciBwcmlvciwgd2UgY2FuIG1vZGVsIHRoaXMgdXNpbmcgYSBiZXRhIGRpc3RyaWJ1dGlvbiBieSBzZXR0aW5nICRhJCBhbmQgJGIkIHRvIGJvdGggZXF1YWwgdG8gMQ0KDQokJA0KcChcdGhldGEpIFxzaW0gXHRleHR7QmV0YX1bMSwxXSBcc2ltIFx0aGV0YV57MS0xfVx0aW1lcygxLVx0aGV0YSleezEtMX09XHRoZXRhXnswfVx0aW1lcygxLVx0aGV0YSleezB9PSAxIFx0ZXh0eyAoZm9yIGFsbCB2YWx1ZXMgb2YgfVx0aGV0YSkNCiQkDQoNCkFuZCB3ZSBjYW4gbW9kZWwgb3VyIGxpa2VsaWhvb2QgdXNpbmcgdGhlIGJpbm9taWFsIGZ1bmN0aW9uDQoNCiQkDQpwKFh8IFx0aGV0YSkgPSBwKFx0ZXh0e0g9fWt8IFx0aGV0YSkgPSBcdGV4dHtCaW5vbX0obixwKSBcc2ltIFx0aGV0YV5rXHRpbWVzKDEtXHRoZXRhKV57bi1rfQ0KJCQNCg0KQW5kIGZpbmFsbHkgLSB0aGUgcG9zdGVyaW9yIGlzIHByb3BvcnRpb25hbCB0byB0aGUgcHJpb3IgbXVsdGlwbGllZCBieSB0aGUgbGlrZWxpaG9vZDoNCg0KJCQNCnAoXHRoZXRhKSBcdGltZXMgcChYfCBcdGhldGEpIFxzaW0gW1x0aGV0YV57YS0xfVx0aW1lcygxLVx0aGV0YSlee2ItMX1dIFx0aW1lcyBbXHRoZXRhXmtcdGltZXMoMS1cdGhldGEpXntuLWt9XSA9IFx0aGV0YV57YS0xK2t9XHRpbWVzKDEtXHRoZXRhKV57Yi0xK24ta30NCiQkDQoNCldoaWNoIHdlIGNhbiBwdXJlbHkgcGFyYW1ldGl6ZSBpbiBhIGJldGEgZGlzdHJpYnV0aW9uICh5b3UgY2FuIHNlZSB0aGF0LCB0aGUgc3Ryb25nZXIgdGhlIHByaW9yIC0gYW5kIGhlbmNlIHRoZSBsYXJnZXIgYSBhbmQgYiAtIHRoZSBzbWFsbGVyIHRoZSBpbXBhY3QgbmV3IGV2aWRlbmNlIGsgYW5kIG4gaGFzIG9uIHRoZSBwb3N0ZXJpb3IgZGlzdHJpYnV0aW9uKToNCg0KJCQNClx0aGV0YV57YS0xK2t9XHRpbWVzKDEtXHRoZXRhKV57Yi0xK24ta30gXHNpbSBcdGV4dHtCZXRhfVthK2ssYituLWtdIA0KJCQNCg0KYGBgIHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9N30NCg0KZ2FtbWFfZnVuY3Rpb24gPC0gZnVuY3Rpb24obikgew0KICByZXR1cm4oZmFjdG9yaWFsKG4tMSkpDQp9DQoNCmJldGFfcHJvYmFiaWxpdHkgPC0gZnVuY3Rpb24odGhldGEsYSxiKSB7DQogIG51bWVyYXRvciA8LSAoKHRoZXRhKV4oYS0xKSkqKCgxLXRoZXRhKV4oYi0xKSkNCiAgZGVub21pbmF0b3IgPC0gKGdhbW1hX2Z1bmN0aW9uKGEpKmdhbW1hX2Z1bmN0aW9uKGIpKS9nYW1tYV9mdW5jdGlvbihhK2IpDQogIHJldHVybihudW1lcmF0b3IvZGVub21pbmF0b3IpDQp9DQoNCmEgPC0gMQ0KYiA8LSAxDQoNCiMgdW5pZm9ybSBkaXN0cmlidXRpb24NCiMgcHJpb3IgPC0gZGJldGEoeCA9IHgsIHNoYXBlMSA9IDEsIHNoYXBlMiA9IDENCnggPC0gc2VxKDAsIDEsIGxlbmd0aD0xMDArMSkNCnByaW9yIDwtIHNhcHBseSh4LCBGVU4gPSBmdW5jdGlvbih0aGV0YSkge2JldGFfcHJvYmFiaWxpdHkodGhldGEsYSxiKX0pDQoNCiMgYmlub21pYWwgZGlzdHJpYnV0aW9uDQpsaWtlbGlob29kX2YuYmlub20gPC0gc2FwcGx5KHgsIEZVTiA9IGZ1bmN0aW9uKHRoZXRhKSB7Ymlub21pYWxfcHJvYmFiaWxpdHkoayxuLHRoZXRhKX0pDQpsaWtlbGlob29kX2YuYmlub20uaW50IDwtIGxpa2VsaWhvb2RfZi5iaW5vbQ0KbGlrZWxpaG9vZF9mLmJpbm9tLmludFtyb3VuZCh4Km4sMCkgIT0geCpuXSA8LSBOQQ0KDQojIGJldGEgZGlzdHJpYnV0aW9uDQpsaWtlbGlob29kX2YuYmV0YSA8LSBzYXBwbHkoeCwgRlVOID0gZnVuY3Rpb24odGhldGEpIHtiZXRhX3Byb2JhYmlsaXR5KHRoZXRhLGsrMSxuLWsrMSkqMS8obisxKX0pDQoNCnBvc3RlcmlvciA8LSBzYXBwbHkoeCwgRlVOID0gZnVuY3Rpb24odGhldGEpIHtiZXRhX3Byb2JhYmlsaXR5KHRoZXRhLGEraysxLGIrbi1rKzEpKjEvKG4rMSl9KQ0KDQpwbG90X2x5KHR5cGUgPSAnc2NhdHRlcicsIG1vZGUgPSAnbGluZXMnKSAlPiUNCiAgYWRkX3RyYWNlKHggPSB4LCB5ID0gcHJpb3Ivc3VtKHByaW9yKSwgbmFtZSA9ICdQcmlvcjogVW5pZm9ybSA9IEJldGEoMSwxKScpICU+JQ0KICBhZGRfdHJhY2UoeCA9IHgsIHkgPSBsaWtlbGlob29kX2YuYmlub20uaW50LCBuYW1lID0gJ0xpa2VsaWhvb2Q6IH4gQmlub20oaz0yLHA9dGhldGEsbiknLCBtb2RlID0gJ21hcmtlcnMnKSAlPiUNCiAgYWRkX3RyYWNlKHggPSB4LCB5ID0gbGlrZWxpaG9vZF9mLmJldGEsIG5hbWUgPSAnTGlrZWxpaG9vZDogfiBCZXRhKHA9dGhldGEsayxuLWspJykgJT4lDQogIGFkZF90cmFjZSh4ID0geCwgeSA9IHBvc3RlcmlvciwgbmFtZSA9ICdQb3N0ZXJpb3I6IH4gQmV0YShhK2ssYituLWspJykgJT4lDQogIGxheW91dCh4YXhpcyA9IGxpc3QodGl0bGUgPSAnVGhldGEnKSkNCmBgYA0KDQpBcyB5b3UgY2FuIHNlZSwgd2l0aCBhIHZlcnkgJ29iamVjdGl2ZScgcHJpb3IsIHRoZSBCYXllc2lhbiBwb3N0ZXJpb3IgaXMgdmVyeSBzaW1pbGFyIHRvIHRoZSByZXN1bHQgZGVyaXZlZCBmcm9tIHRoZSBmcmVxdWVudGlzdCBldmlkZW5jZS1vbmx5IGRpc3RyaWJ1dGlvbiAodGhlIEJheWVzaWFuIGxpa2VsaWhvb2QgZnVuY3Rpb24pLg0KDQojIyBNYXJrb3YtY2hhaW4gTW9udGUtQ2FybG8gKE1DTUMpOiBFc3RpbWF0aW5nIHBvc3RlcmlvciBkaXN0cmlidXRpb25zIHdoZW4gdGhlIHByaW9yL2xpa2VsaWhvb2QgZGVuc2l0eSBmdW5jdGlvbnMgYXJlIG5vbi1jb25qdWdhbnQNCg0KU29tZXRpbWVzIHRob3VnaCwgd2UgaGF2ZSBwcmlvcnMgYW5kIGxpa2VsaWhvb2QgZnVuY3Rpb25zIHRoYXQgZG8gbm90IHBsYXkgbmljZWx5IHdpdGggZWFjaCBvdGhlciwgYW5kIGhlbmNlIHdlIGNhbm5vdCBkZWZpbmUgdGhlIHBvc3RlcmlvciBkZW5zaXR5IGZ1bmN0aW9uIGFuYWx5dGljYWxseS4NCkluIHRoaXMgaW5zdGFuY2UsIHdlIGNhbiBtYWtlIG1hbnkgZHJhd3MgZnJvbSB0aGUgcG9zdGVyaW9yIGRpc3RyaWJ1dGlvbiBmb3IgdmFyaW91cyB2YWx1ZXMgb2YgJFx0aGV0YSQgdG8gaW5mZXIgd2hhdCB0aGUgcG9zdGVyaW9yIGRpc3RyaWJ1dGlvbiBsb29rcyBsaWtlLg0KDQpUaGlzIGlzIHRoZSAnbW9udGUtY2FybG8nIGJpdCAtIHdlIHJhbmRvbWx5IGNob29zZSB2YWx1ZXMgb2YgJFx0aGV0YSQgdG8gbWFrZSBkcmF3cyBmcm9tIHRoZSBwb3N0ZXJpb3IgZnJvbS4gDQoNCkhvd2V2ZXIsIHRoZSBhaW0gb2YgdGhpcyBpcyB0byBjcmVhdGUgYSBoaXN0b2dyYW0gdGhhdCBtb3N0IGNsb3NlbHkgcmVzZW1ibGVzIHRoZSBwb3N0ZXJpb3IgZGlzdGlidXRpb24gdGhhdCB3ZSBhcmUgdHJ5aW5nIHRvIGVzdGltYXRlLg0KRm9yIHRoaXMgaGlzdG9ncmFtIHRvIGJlIHJlZmxlY3RpdmUgb2YgdGhlIHBvc3RlcmlvciBkaXN0cmlidXRpb24sIHdlIG5lZWQgdG8gdGFrZSBkcmF3cyBtb3JlIGZyZXF1ZW50bHkgZnJvbSB0aGUgYXJlYXMgaGlnaGVyIGRlbnNpdHkgZnJvbSB0aGUgcG9zdGVyaW9yLCBhbmQgbGVzcyBmcmVxdWVudGx5IHdoZXJlIHRoZXkgYXJlIG9mIGxvd2VyIGRlbnNpdHkuDQoNClRoaXMgaXMgd2hlcmUgdGhlICdtYXJrb3YtY2hhaW4nIGJpdCBjb21lcyBpbiAtIHdoZW4gbWFraW5nIGEgbmV3IGRyYXcsIHdoZXRoZXIgd2UgYWRkIHRoaXMgbmV3IHZhbHVlIHRvIHRoZSBoaXN0b2dyYW0sIG9yIHRoZSBvbGQgb25lIGFnYWluIHRvIHRoZSBoaXN0b2dyYW0sIGRlcGVuZHMgb24gdGhlIHByb2JhYmlsaXR5IGRlbnNpdHkgYXNzb2NpYXRlZCB3aXRoIHRoZSBuZXcgcG9pbnQgdnMgdGhlIG9sZCBvbmUuDQoNClRoZSBpbnN0cnVjdGlvbnMgZm9yIHRoZSBNQ01DIGFsZ29yaXRobSAodXNpbmcgdGhlIE1ldHJvcG9saXMtSGFzdGluZ3MgbWV0aG9kKSBpcyBhcyBmb2xsb3dzOg0KDQokJA0KXHRleHR7Zm9yKGkgaW4gbnVtX2RyYXdzKSB9IFx7XFwNClxoc3BhY2V7M2NtfVx0aGV0YV97bmV3LGl9Oj0gWCBcc2ltIE4oXHRoZXRhX3tyZWNvcmRlZCxpLTF9LFxzaWdtYV4yKSBcXA0KXGhzcGFjZXszY219cG9zdGVyaW9yX3tuZXcsaX06PSBQKFh8XHRoZXRhX3tuZXcsaX0pIFx0aW1lcyBQKFx0aGV0YV97bmV3LGl9KSBcXA0KXGhzcGFjZXszY219cG9zdGVyaW9yX3tvbGQsaX06PSBQKFh8XHRoZXRhX3tvbGQsaX0pIFx0aW1lcyBQKFx0aGV0YV97b2xkLGl9KSBcXA0KXGhzcGFjZXszY219cl97bnVtLGl9Oj0gcl9pIFxzaW0gXHRleHR7dW5pZn0oMCwxKVxcDQpcaHNwYWNlezNjbX0gXHRleHR7aWZ9XGxlZnQoXGZyYWN7cG9zdGVyaW9yX3tuZXcsaX19e3Bvc3Rlcmlvcl97b2xkLGl9fSA+IHJfe251bSxpfVxyaWdodCkgXHtcXA0KXGhzcGFjZXs2Y219XHRoZXRhX3tyZWNvcmRlZCxpfTo9XHRoZXRhX3tuZXcsaX1cXA0KXGhzcGFjZXs2Y219XHRoZXRhX3tyZWNvcmRlZCxpfTo9XHRoZXRhX3tvbGQsaX1cXA0KXGhzcGFjZXszY219XH0NClxcDQpcfQ0KJCQNCg0KV2hhdCBkb2VzIHRoaXMgbWVhbj8NCg0KKiBXaXRoIGVhY2ggdmFsdWUgb2YgJFx0aGV0YV97cmVjb3JkZWQsaS0xfSQgd2UgcmVjb3JkIGZyb20gdGhlIHByZXZpb3VzIGl0ZXJhdGlvbiwgd2UgdGFrZSBhIHJhbmRvbSB3YWxrIGZyb20gdGhhdCBwb2ludCB0byB0cmlhbCBhbm90aGVyIGNhbmRpZGF0ZSAkXHRoZXRhX3tuZXcsaX0kIChkcmF3aW5nIGZyb20gYSBub3JtYWwgZGlzdHJpYnV0aW9uIHdpdGggbWVhbiBnaXZlbiBhcyB0aGUgcHJldmlvdXNseSByZWNvcmRlZCAkXHRoZXRhX3tyZWNvcmRlZCxpLTF9JCwgYW5kIGZpeGVkIHN0YW5kYXJkIGRldmlhdGlvbiAkXHNpZ21hXjIkKQ0KKiBJZiB0aGF0IGFyZWEgaXMgb2YgaGlnaGVyIGRlbnNpdHkgdGhhbiB0aGUgcHJldmlvdXMgb25lIChzbyB0aGUgcmF0aW8gaXMgMSBvciBncmVhdGVyKSB0aGVuIHdlIHJlY29yZCB0aGF0DQoqIEhvd2V2ZXIsIHdlIGRvbid0IGF1dG9tYXRpY2FsbHkgcmVqZWN0IGl0LiBJZiBpdCBpcyBiaWdnZXIgdGhhbiBhIG51bWJlciByYW5kb21seSBnZW5lcmF0ZWQgZnJvbSBhIHVuaWZvcm0gZGlzdHJpYnV0aW9uICgkcl9pJCkgdGhlbiB3ZSBzdGlsbCBhY2NlcHQgaXQgKHRvIGFsbG93IGlmIHRvIGV4cGxvcmUgdGhlIGRpc3RyaWJ1dGlvbiByYXRoZXIgdGhhbiBnZXQgc3R1Y2sgaW4gbG9jYWwgbWF4aW1hKQ0KKiBXZSBjYW4gdGhlbiBtYWtlIGEgaGlzdG9ncmFtIG9mIGFsbCB0aGUgcmVjb3JkZWQgJFx0aGV0YV97cmVjb3JkZWR9JCBhY3Jvc3MgYWxsIGl0ZXJhdGlvbnMsIGFuZCBpdCBzaG91bGQgbWlycm9yIHRoZSB0cnVlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgcG9zdGVyaW9yLg0KDQpMZXQncyB0YWtlIG91ciBleGFtcGxlIGJlZm9yZSwgd2hlcmUgb3VyIHByaW9yIGhhZCBgciBwKm5gIG9mIGByIG5gIGZsaXBzLCBhbmQgb3VyIGV2aWRlbmNlIHdhcyBgciBrYCBvZiBgciBuYCBmbGlwcywgdG8gY2hlY2sgaG93IHdlbGwgdGhpcyB3b3Jrcy4gV2UgcnVuIHRoZSBhbGdvcml0aG0gMTAsMDAwIHRpbWVzIChpbiBnZW5lcmFsIHVzZSwgd2UgbWlnaHQgZXhjbHVkZSBhIGxvdCBmaXJzdCBkcmF3cywgY2FsbGVkIHRoZSBidXJuLCBidXQgaW4gdGhpcyBpbnN0YW5jZSBhcyBsb25nIGFzIHdlIHRha2UgZW5vdWdoIHNhbXBsZXMgaXQgc2hvdWxkIHN0aWxsIGNyZWF0ZSBhIHJlZmxlY3RpdmUgaGlzdG9ncmFtKS4NCg0KQXMgYSByZW1pbmRlcjogDQoNCiogVGhlIHByaW9yIHdhcyAkXHRleHR7QmV0YX1bazErMSxuMS1rMSsxXT1cdGV4dHtCZXRhfVtgciBrMSsxYCxgciBuMS1rMSsxYF0kDQoqIFRoZSBsaWtlbGlob29kIHdhcyAkXHRleHR7Qmlub219W2ByIGsyYCxgciBrMi9uMmBdJCANCiogU28gd2UgZXhwZWN0IHRoZSBwb3N0ZXJpb3IgJFxzaW0gXHRleHR7QmV0YX1bazErazIrMixuMStuMi1rMS1rMisyXT1cdGV4dHtCZXRhfVtgciBrMStrMisxYCxgciBuMStuMi1rMS1rMisyYF0kKToNCg0KYGBgIHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9N30NCiMjIE1DTUMgTS1IDQp0aGV0YV92IDwtIGMoKQ0KZm9yKGkgaW4gMToxMF41KSB7DQogIGlmKGkgPT0gMSkgew0KICAgIHRoZXRhX29sZCA8LSBybm9ybSgxLHAsMC4wNSkNCiAgICB0aGV0YV9uZXcgPC0gcm5vcm0oMSxwLDAuMDUpDQogIH0gZWxzZSB7DQogICAgdGhldGFfb2xkIDwtIGNob3Nlbl90aGV0YQ0KICB9DQogIHRoZXRhX25ldyA8LSBybm9ybSgxLHRoZXRhX29sZCwwLjA1KQ0KICBpZih0aGV0YV9uZXcgPCAwIHwgdGhldGFfbmV3ID4gMSkgew0KICAgIGJyZWFrDQogIH0NCiAgcG9zdGVyaW9yX25ldyA8LSBkYmV0YSh0aGV0YV9uZXcsazErMSxuMS1rMSsxKSAqIGRiaW5vbShrMixuMix0aGV0YV9uZXcpICMgbmV3IHByaW9yICogbGlrZWxpaG9vZA0KICBwb3N0ZXJpb3Jfb2xkIDwtIGRiZXRhKHRoZXRhX29sZCxrMSsxLG4xLWsxKzEpICogZGJpbm9tKGsyLG4yLHRoZXRhX29sZCkgIyBvbGQgcHJpb3IgKiBsaWtlbGlob29kDQogIGlmKHBvc3Rlcmlvcl9uZXcvcG9zdGVyaW9yX29sZCA+IHJ1bmlmKDEpKSB7DQogICAgY2hvc2VuX3RoZXRhIDwtIHRoZXRhX25ldw0KICB9IGVsc2Ugew0KICAgIGNob3Nlbl90aGV0YSA8LSB0aGV0YV9vbGQNCiAgfQ0KICB0aGV0YV92IDwtIGModGhldGFfdixjaG9zZW5fdGhldGEpDQp9DQoNCnBsb3RfbHkoKSAlPiUNCiAgYWRkX3RyYWNlKHg9dGhldGFfdix0eXBlPSdoaXN0b2dyYW0nLG5hbWU9J2ZyZXF1ZW5jeScpICU+JQ0KICBhZGRfdHJhY2UoeD1kZW5zaXR5KHRoZXRhX3YpJHgsIHk9ZGVuc2l0eSh0aGV0YV92KSR5LHR5cGU9J3NjYXR0ZXInLG1vZGU9J2xpbmVzJyx5YXhpcz0neTInLG5hbWU9J0RlbnNpdHkgYXBwcm94aW1hdGlvbicpICU+JQ0KICBhZGRfdHJhY2UoeD1zZXEoMCwxLDAuMDEpLCB5PWRiZXRhKHNlcSgwLDEsMC4wMSksazErazIrMixuMStuMi1rMS1rMisyKSx0eXBlPSdzY2F0dGVyJyxtb2RlPSdsaW5lcycseWF4aXM9J3kyJyxuYW1lPXBhc3RlMCgnVHJ1ZSBwb3N0ZXJpb3I6IEJldGEoJyxrMStrMisyLCcsJyxuMS1rMStuMi1rMisyLCcpJykpICU+JQ0KICBsYXlvdXQoeWF4aXMyPWxpc3Qob3ZlcmxheWluZz0ieSIsc2lkZT0icmlnaHQiLHJhbmdlbW9kZSA9ICJ0b3plcm8iKSkNCmBgYA0KDQoNCiMjIFRoaW5raW5nIGFib3V0IEJheWVzaWFuIGluIHRlcm1zIG9mIHByZWRpY3RpbmcgZ2V0dGluZyBhIGhlYWQgbmV4dCB0aW1lIChjb250aW51b3VzIHByaW9yKToNCg0KU28gbm93IHdlIGFyZSBzZXQgdXAgdG8gbWFrZSBhIHByZWRpY3Rpb24gZm9yIGdldHRpbmcgYSBoZWFkcyBuZXh0IHRpbWUsIGdpdmVuIG91ciBwcmlvciBiZWxpZWZzIGFuZCBvdXIgbmV3IGV2aWRlbmNlLg0KDQpMZXQncyByZW1pbmQgb3Vyc2VsdmVzIG9mIHRoZSBlcXVhdGlvbiB0byBtYWtlIHByZWRpY3Rpb25zOg0KDQokJA0KZih5fHgpPSBcaW50IGYoeXxcdGhldGEseClmKFx0aGV0YXx4KSBccGFydGlhbChcdGhldGEpDQokJA0KDQpXZSBub3cga25vdyAkcChcdGhldGF8eCkkIC0gb3VyIHBvc3RlcmlvciBkaXN0cmlidXRpb24uIA0KU28gbm93IHdlIGp1c3QgbmVlZCB0byBlc3RpbWF0ZSAkcCh5fFx0aGV0YSx4KSQuIFNvIGhvdyBkbyB3ZSBkbyB0aGlzPw0KDQpXZWxsIHdlIGtub3cgdGhhdCAkeSQgZm9sbG93cyBhIGJlcm5vdWxsaSBkaXN0cmlidXRpb24sIGFuZCB3ZSBjYW4gc29sdmUgdGhpcyB0aHJvdWdoIG1heGltdW0gbGlrZWxpaG9vZCBlc3RpbWF0aW9uLg0KSW4gcGFydGljdWxhciwgZm9yIGJpbmFyeSBwcmVkaWN0aW9ucywgd2Ugb2Z0ZW4gYXNzdW1lIHRoYXQgdGhlIGxpa2VsaWhvb2QgZnVuY3Rpb24gZm9sbG93cyBhIGxvZ2lzdGljIGRpc3RyaWJ1dGlvbiwgZ2l2ZW4gYXMgJHAoeXxcdGhldGEsWCk9KDErZV57LVhcYmV0YX0pXnstMX0kIC0gKio8YSBocmVmPSJodHRwczovL2NocmlzLWtlbGx5LmdpdGh1Yi5pby9kc19ibG9nL2xvZ2l0LWZyb20tbWwvbG9naXQtY29lZmZpY2llbnRzLmh0bWwiPllvdSBjYW4gc2VlIHRoaXMgaW4gYW5vdGhlciBub3RlIEkgaGF2ZSB3cml0dGVuIGhlcmU8L2E+KiouDQoNCkdyZWF0LCBzbyBqdXN0IG5lZWQgdG8gaW50ZWdyYXRlIHRoZSBwcm9kdWN0IG9mICRmKHl8XHRoZXRhLHgpJCBhbmQgJGYoXHRoZXRhfHgpJCB3aXRoIHJlc3BlY3QgdG8gJFx0aGV0YSQuDQoNCmBgYCB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTd9DQp5X2Rpc3QgPC0gYygpDQp0aGV0YV9vcHRpb25zIDwtIHNlcSgwLDEsMC4wMSkNCmZvcihpIGluIHRoZXRhX29wdGlvbnMpIHsNCiAgdGhldGFfcG9zdGVyaW9yX2Rpc3QgPSBkYmV0YShpLGsxK2syKzIsbjEtazErbjItazIrMikNCiAgeV9sb2dpdCA9ICgxK2V4cCgtKGsyKSp0aGV0YV9wb3N0ZXJpb3JfZGlzdCkpXigtMSkNCiAgeV9kaXN0IDwtIGMoeV9kaXN0LHRoZXRhX3Bvc3Rlcmlvcl9kaXN0KnlfbG9naXQpDQp9DQoNCnBsb3RfbHkoKSAlPiUNCiAgYWRkX3RyYWNlKHg9c2VxKDAsMSwwLjAxKSwgeT15X2Rpc3QsdHlwZT0nc2NhdHRlcicsbW9kZT0nbGluZXMnLG5hbWU9cGFzdGUwKCdQKHl8WCknKSkgJT4lDQogIGxheW91dCh0aXRsZT0nTGlrZWxpaG9vZCBvZiBvYnNlcnZpbmcgaGVhZHMgbmV4dCB0aW1lJykNCg0KYGBgDQoNClRoaXMgaXMgbW9yZSBpbnRlcmVzdGluZyBpZiB3ZSBoYWQgdmFyeWluZyBleHBsYW5hdG9yeSB2YXJpYWJsZXMgJFgkIC0gYW5kIHRoZW4gd2UgY2FuIGRlcml2ZSBkaWZmZXJlbnQgcG9zdGVyaW9yIGRpc3RyaWJ1dGlvbnMgZm9yICR5JCBnaXZlbiBkaWZmZXJlbnQgdmFsdWVzIGZvciB0aG9zZSBleHBsYW5hdG9yeSBmZWF0dXJlcy4NCg0KDQojIEJheWVzaWFuIENyZWRpYmxlIGludGVydmFscyBhbmQgSFBEIChuZWVkIHRvIGNoYW5nZSB0aGlzIHRvICR5JCkNCg0KU28gbm93IHdlIGhhdmUgb3VyIHBvc3RlcmlvciBkaXN0cmlidXRpb24gZm9yICRceSQsIGhvdyBtaWdodCB3ZSBzdW1tYXJpc2Ugb3VyIG5ldyB1bmRlcnN0YW5kaW5nIGFzIHRvIHdoZXJlICRceSQgbWlnaHQgbGllIGluIHRoZSByYW5nZSBvZj8NCg0KVGhpcyBsZWFkcyBvbiB0byAqKkJheWVzaWFuIENyZWRpYmxlIGludGVydmFscyoqIC0gdGhlIEJheWVzaWFuIGVxdWl2YWxlbnQgdG8gZnJlcXVlbnRpc3QncyBjb25maWRlbmNlIGludGVydmFscy4gQmVjYXVzZSBCYXllc2lhbiBlc3RpbWF0ZXMgZGlzdHJpYnV0aW9ucyByYXRoZXIgdGhhbiBmaXhlZCB2YWx1ZXMsIGNyZWRpYmxlIGludGVydmFscyBhcmUgYXJndWFibHkgbW9yZSBpbnR1aXRpdmUgdGhhbiBjb25maWRlbmNlIGludGVydmFsczogdGhleSByZWZsZWN0IHRoZSBhICg5NSUpIHByb2JhYmlsaXR5IHRoYXQgb2YgaGVyZSB0aGUgdmFsdWUgZ2VuZXJhdGVkIGJ5IHRoZSByYW5kb20gdmFyaWFibGUgdGhldGEgd2lsbCBmYWxsIHdpdGhpbiB0aGUgcmFuZ2UgKHJhdGhlciB0aGFuIHRoZSBmcmVxdWVudGlzdCBpbnRlcnByZXRhdGlvbiB0aGF0IHRoZSB0cnVlIHZhbHVlIHdpbGwgZmFsbCB3aXRoaW4gdGhlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgOTUlIG9mIHRoZSB0aW1lKS4NCg0KTGlrZSBhIGNvbmZpZGVuY2UgaW50ZXJ2YWwsIGEgY3JlZGlibGUgaW50ZXJ2YWwgaXMgc3ltbWV0cmljIGFyb3VuZCB0aGUgbWVhbiwgYW5kIHNwYW5zIHRoZSB4LWF4aXMgZW5vdWdoIHRvIGNvdmVyIDk1JSBvZiB0aGUgYXJlYSB1bmRlciB0aGUgcHJvYmFiaWxpdHkgZGlzdHJpYnV0aW9uIGN1cnZlLiBBbmQgdGhhdCBpcyBnb29kIGlmIHRoZSBwb3N0ZXJpb3IgZGlzdHJpYnV0aW9uIGlzIGFsc28gc3ltbWV0cmljIC0gYnV0IGFzIGNhbiBiZSBzZWVuIGluIG91ciBleGFtcGxlIGFib3ZlLCB0aGF0IGlzIG5vdCBuZWNlc3NhcmlseSB0aGUgY2FzZSwgd2hlcmUgdGhlIHBvc3RlcmlvciBpcyBza2V3ZWQuDQoNCklmIHRoZSBkaXN0cmlidXRpb24gaXMgc2tld2VkIHRoZW4sIGl0IGNvdWxkIGJlIGJldHRlciB0byBjb21wdXRlIHRoZSByZWdpb24gb2YgKipoaWdoZXN0IHBvc3RlcmlvciBkZW5zaXR5IChIUEQpKiogLSB3aGljaCBieSBkZWZpbml0aW9uIHdpbGwgYmUgdGhlIG5hcnJvd2VzdCBpbnRlcnZhbCBhY3Jvc3MgdGhlIHBvdGVudGlhbCB2YWx1ZXMgZm9yIHRoZXRhLCBidXQgY292ZXIgOTUlIG9mIHRoZSBhcmVhIHVuZGVyIHRoZSBwcm9iYWJpbGl0eSBjdXJ2ZS4gSW4gb3RoZXIgd29yZHMsIHdlIHN0aWxsIG1haW50YWluIHRoZSBzYW1lIDk1JSBwcm9iYWJpbGl0eSBvZiB0aGV0YSBseWluZyB3aXRoaW4gdGhlIHJlZ2lvbiwgYnV0IHRoZSByZWdpb24gaXMgdGhlIG5hcnJvd2VzdCBpdCBwb3NzaWJseSBjYW4gYmUuDQoNClNvIHRvIGRvIHRoaXMsIHdlIHdhbnQgdG8gZmluZCB0aGUgaW50ZXJ2YWwgYmV0d2VlbiAkXHRoZXRhX3tsb3d9JCBhbmQgJFx0aGV0YV97aGlnaH0kIHN1Y2ggYXQ6DQoNCiQkDQpcaW50X3tcdGhldGFfe2xvd319XntcdGhldGFfe2hpZ2h9fSBwKFx0aGV0YXx4KSBkXHRoZXRhPSAxLVxhbHBoYQ0KJCQNCg0KQnV0IGZpbmRpbmcgdGhpcyBjYW4gYmUgdHJpY2t5LCBwYXJ0aWN1bGFybHkgd2l0aCBjb21wdXRpbmcgY2xvc2VkIGludGVydmFscy4gVGhlIGNvZGUgYmVsb3cgc2hvd3MgYSBicnV0ZSBmb3JjZSBzb2x1dGlvbi4gVGhlIHdheSB0byB0aGluayBhYm91dCB0aGlzIGlzIGFzIGZvbGxvd3M6DQoNCiogQ3JlZGlibGUgaW50ZXJ2YWxzOiBHaXZlbiB0aGUgbWVhbiBvZiB0aGUgZGlzdHJpYnV0aW9uLCBleHBhbmQgdGhlIHJhbmdlIG91dHdhcmRzIGVxdWFsbHkgdW50aWwgNTAlIG9mIHRoZSBhcmVhIHVuZGVyIHRoZSBjdXJ2ZSBpcyBjYXB0dXJlZC4NCiogSGlnaGVzdCBwb3N0ZXJpb3IgZGVuc2l0eTogSW1hZ2luZSBhIGhvcml6b250YWwgbGluZSBtb3Zpbmcgc2xvd2x5IHRvd2FyZHMgdGhlIHggYXhpcywgaW50ZXJjZXB0aW5nIHRoZSBkaXN0cmlidXRpb24gdHdpY2UuIFRoZSBsaW5lIHN0b3BzIGRlc2NlbmRpbmcgb25jZSBpdCBpcyBsb3cgZW5vdWdoIHRoYXQgdGhlIGF1YyBpcyA1MCUuDQoNCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NywgZmlnLmZ1bGx3aWR0aCA9IFRSVUV9DQoNCmZpbmRfYmV0YV9jcmVkaWJsZV9pbnRlcnZhbCA8LSBmdW5jdGlvbihzMSxzMixsZWFybmluZ19yYXRlID0gMC4wNSwgaW5pdGlhbF93aWR0aCA9IDAuMSwgY3JpdGVyaW9uID0gMC4wNSwgbWF4X2l0ZXJhdGlvbnMgPSAxMDAwLCBwcmVjaXNpb24gPSAwLjAwMSkgew0KICBtZWFuX3AgPC0gc3VtKHNhcHBseShzZXEoMCwxLDEvbWF4X2l0ZXJhdGlvbnMpLCBGVU4gPSBmdW5jdGlvbih4KSB7ZGJldGEoeCxzMSxzMil9KSpzZXEoMCwxLDEvbWF4X2l0ZXJhdGlvbnMpKS8NCiAgICAgICAgICAgICBzdW0oc2FwcGx5KHNlcSgwLDEsMS9tYXhfaXRlcmF0aW9ucyksIEZVTiA9IGZ1bmN0aW9uKHgpIHtkYmV0YSh4LHMxLHMyKX0pKQ0KICBiaXQgPC0gaW5pdGlhbF93aWR0aC8yICMgYW1vdW50IHRvIHB1c2ggYXdheSBmcm9tIG1lYW4NCiAgZm9yKGkgaW4gMTptYXhfaXRlcmF0aW9ucykgew0KICAgIGFtb3VudCA8LSBwYmV0YShtZWFuX3ArYml0LHMxLHMyKSAtIHBiZXRhKG1lYW5fcC1iaXQsczEsczIpICMgc3VtIHRoZSBhdWMgYmV0d2VlbiBwLWJpdCBhbmQgcCtiaXQNCiAgICBpZihhYnMoYW1vdW50IC0gKDEtY3JpdGVyaW9uKSkgPCBwcmVjaXNpb24pIHsNCiAgICAgIGJyZWFrKCkNCiAgICB9IGVsc2Ugew0KICAgICAgYml0IDwtIGJpdCAtIGxlYXJuaW5nX3JhdGUqKGFtb3VudCAtICgxLWNyaXRlcmlvbikpICMgZGVjcmVhc2UgaWYgYXVjIG1lZXRzIGNyaXRlcmlhIA0KICAgIH0NCiAgfQ0KICByZXR1cm4oYyhtZWFuX3AtYml0LG1lYW5fcCtiaXQpKQ0KfQ0KDQpjdW1zdW1fcGJldGEgPSBmdW5jdGlvbih2LHMxLHMyKSB7DQogIHNhcHBseSh2LEZVTiA9IGZ1bmN0aW9uKHgpIHBiZXRhKHgsczEsczIpLXBiZXRhKHZbMV0sczEsczIpKQ0KfQ0KDQpmaW5kX2JldGFfaHBkIDwtIGZ1bmN0aW9uKHMxLHMyLGNvbmZfcmFuZ2U9MC45NSxwcmVjaXNpb24gPSAwLjAwMSkgew0KICByZXN1bHRzIDwtIA0KICAgIHNhcHBseShzZXEoMSwxKzEvcHJlY2lzaW9uLDEpLCBGVU4gPSBmdW5jdGlvbihpKSB7DQogICAgICB0aGlzX2JpdCA8LSBzZXEoc2VxKDAsMSxwcmVjaXNpb24pW2ldLDEscHJlY2lzaW9uKSAjIGZvciBhbGwgcmFuZ2UgYmV0d2VlbiB2W2ldIGFuZCAxDQogICAgICBpZihtYXgoY3Vtc3VtX3BiZXRhKHRoaXNfYml0LHMxLHMyKSkgPiBjb25mX3JhbmdlKSB7ICMgZGlzcmVnYXJkIGlmIG5vdCBiaWcgZW5vdWdoIGZvciBjb25mIGludGVydmFsDQogICAgICAgIHRoaXNfYml0W2N1bXN1bV9wYmV0YSh0aGlzX2JpdCxzMSxzMikgPD0gY29uZl9yYW5nZV0gIyBzdG9yZSBzbWFsbGVzdCByYW5nZSB0aGF0IGlzIGp1c3QgYmVsb3cgY29uZl9yYW5nZQ0KICAgICAgfSBlbHNlIHsNCiAgICAgICAgc2VxKDAsMSxwcmVjaXNpb24pICMgYXJicml0YXJpbHkgdG9vIGxvbmcgdmVjdG9yDQogICAgICB9DQogICAgfSkNCiAgaW5kZXhfY2hvaWNlcyA8LSByZXN1bHRzW21pbih1bmxpc3QobGFwcGx5KHJlc3VsdHMsIEZVTiA9IGxlbmd0aCkpKSANCiAgICAgICAgICAgICAgICAgICAgICAgICAgID09IHVubGlzdChsYXBwbHkocmVzdWx0cywgRlVOID0gbGVuZ3RoKSldICMgb25seSBrZWVwIHRoZSBzbWFsbGVzdCB2ZWN0b3JzIA0KICAjIGlmIGxvdyBwcmVjaXNpb24sIG1hbnkgY2hvaWNlcw0KICBpbmRleGVzIDwtIGluZGV4X2Nob2ljZXNbd2hpY2gubWluKHNhcHBseShpbmRleF9jaG9pY2VzLCBGVU4gPSBmdW5jdGlvbih4KSB7YWJzKGRiZXRhKG1heCh4KSxrKzEsbi1rKzEpLWRiZXRhKG1pbih4KSxrKzEsbi1rKzEpKX0pKV1bWzFdXSAjIGNob29zZSB0aGUgb25lIHdpdGggdGhlIG1vc3QgZXF1YWwgaGVpZ2ggYm91bmRhcmllcw0KICByZXR1cm4oaW5kZXhlcykNCn0NCg0KY3JlZGlibGVfaW50ZXJ2YWxzIDwtIGZpbmRfYmV0YV9jcmVkaWJsZV9pbnRlcnZhbChrMStrMisyLCBuMS1rMStuMi1rMisyLCBtYXhfaXRlcmF0aW9ucyA9IDEwXjMsIGNyaXRlcmlvbiA9IDAuMDUpDQpoaWdoZXN0X3Bvc3Rlcmlvcl9kZW5zaXR5IDwtIGZpbmRfYmV0YV9ocGQoazErazIrMixuMS1rMStuMi1rMisyLGNvbmZfcmFuZ2UgPSAwLjk1KQ0KDQpwbG90X2x5KHR5cGUgPSAnc2NhdHRlcicsIG1vZGUgPSAnbGluZXMnKSAlPiUNCiAgYWRkX3RyYWNlKHggPSB4LCB5ID0geV9kaXN0LCBuYW1lID0gJ0JldGEoOSwxNSknKSAlPiUNCiAgYWRkX3RyYWNlKHggPSByZXAoMC4zNiwyKSwgeSA9IGMoMCxtYXgoc2FwcGx5KGhpZ2hlc3RfcG9zdGVyaW9yX2RlbnNpdHksIEZVTiA9IGZ1bmN0aW9uKHgpIHtkYmV0YSh4LGsxK2syKzIsbjEtazErbjItazIrMil9KSkpLCBuYW1lID0gJ01lYW4nKSAlPiUNCiAgYWRkX3RyYWNlKHggPSBwbWluKHBtYXgoeCwgY3JlZGlibGVfaW50ZXJ2YWxzWzFdKSxjcmVkaWJsZV9pbnRlcnZhbHNbMl0pLCB5ID0gbGlrZWxpaG9vZF9mLCBuYW1lID0gJ0NyZWRpYmxlIEludGVydmFsJywgZmlsbCA9ICd0b3plcm95JywgbGluZSA9IGxpc3QoZGFzaCA9ICdkb3QnKSkgJT4lDQogIGxheW91dCh0aXRsZSA9ICdDcmVkaWJsZSBJbnRlcnZhbCAoZXF1aWRpc3RhbnQgYXJvdW5kIG1lYW4pJykNCg0KcGxvdF9seSh0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ2xpbmVzJykgJT4lDQogIGFkZF90cmFjZSh4ID0gc2VxKDAsMSwwLjAwMSksIHkgPSBzYXBwbHkoc2VxKDAsMSwwLjAwMSksIEZVTiA9IGZ1bmN0aW9uKHgpIHtkYmV0YSh4LGsxK2syKzIsbjEtazErbjItazIrMil9KSwgbmFtZSA9ICdCZXRhKDksMTUpJykgJT4lDQogIGFkZF90cmFjZSh4ID0gcmVwKDAuMzYsMiksIHkgPSBjKDAsbWF4KHNhcHBseShoaWdoZXN0X3Bvc3Rlcmlvcl9kZW5zaXR5LCBGVU4gPSBmdW5jdGlvbih4KSB7ZGJldGEoeCxrMStrMisyLG4xLWsxK24yLWsyKzIpfSkpKSwgbmFtZSA9ICdNZWFuJykgJT4lDQogIGFkZF90cmFjZSh4ID0gaGlnaGVzdF9wb3N0ZXJpb3JfZGVuc2l0eSwgeSA9IHNhcHBseShoaWdoZXN0X3Bvc3Rlcmlvcl9kZW5zaXR5LCBGVU4gPSBmdW5jdGlvbih4KSB7ZGJldGEoeCxrMStrMisyLG4xLWsxK24yLWsyKzIpfSksIG5hbWUgPSAnSGlnaGVzdCBQb3N0ZXJpb3IgRGVuc2l0eScsIGZpbGwgPSAndG96ZXJveScsIGxpbmUgPSBsaXN0KGRhc2ggPSAnZG90JykpICU+JQ0KICBsYXlvdXQodGl0bGUgPSAnSGlnaGVzdCBQb3N0ZXJpb3IgRGVuc2l0eSAoc21hbGxlc3Qgc3ByZWFkIHRoYXQgY29udGFpbnMgOTUlIG9mIGRhdGEpJykNCiAgDQpgYGANCg0KVGhlIHdpZHRoIG9mIHRoZSBjcmVkaWJsZSBpbnRlcnZhbCBpcyBsYXJnZXIsIHJhbmdpbmcgZnJvbSBgciBtaW4oY3JlZGlibGVfaW50ZXJ2YWxzKWAgdG8gYHIgbWF4KGNyZWRpYmxlX2ludGVydmFscylgIChyYW5nZSBvZiBgciBtYXgoY3JlZGlibGVfaW50ZXJ2YWxzKSAtIG1pbihjcmVkaWJsZV9pbnRlcnZhbHMpYCksIHdoZXJlYXMgd2lkdGggdGhlIHJhbmdlIG9mIHRoZSBIUEQgaXMgc21hbGxlciwgcmFuZ2luZyBmcm9tIGByIG1pbihoaWdoZXN0X3Bvc3Rlcmlvcl9kZW5zaXR5KWAgdG8gYHIgbWF4KGhpZ2hlc3RfcG9zdGVyaW9yX2RlbnNpdHkpYCAocmFuZ2Ugb2YgYHIgbWF4KGhpZ2hlc3RfcG9zdGVyaW9yX2RlbnNpdHkpIC0gbWluKGhpZ2hlc3RfcG9zdGVyaW9yX2RlbnNpdHkpYCkNCg0KU29tZSBCYXllc2lhbnMgYXJndWUgdGhvdWdoIHRoYXQgY3JlZGlibGUgaW50ZXJ2YWxzIGFuZCBIUERzIGFyZSBvbmx5IHJlYWxseSBzb21ldGhpbmcgdGhhdCBhcmUgdXNlZnVsIHRvIGNvbXBhcmUgdG8gZnJlcXVlbnRpc3QgaW50ZXJwcmV0YXRpb25zLCBhcyB0aGUgZGlzdHJpYnV0aW9uIGlzIGFscmVhZHkgZ2l2ZW4gYnkgdGhlIHBvc3Rlcmlvci4NCg0KDQoNCg0KDQoNCg==